/* The uses I can see for these HSR supervision frames are: * 1) Use the frames that are sent after node initialization ("HSR_TLV.Type = * 22") to reset any sequence_nr counters belonging to that node. Useful if * the other node's counter has been reset for some reason. * -- * Or not - resetting the counter and bridging the frame would create a * loop, unfortunately. * * 2) Use the LifeCheck frames to detect ring breaks. I.e. if no LifeCheck * frame is received from a particular node, we know something is wrong. * We just register these (as with normal frames) and throw them away. * * 3) Allow different MAC addresses for the two slave interfaces, using the * MacAddressA field.
*/ staticbool is_supervision_frame(struct hsr_priv *hsr, struct sk_buff *skb)
{ struct ethhdr *eth_hdr; struct hsr_sup_tag *hsr_sup_tag; struct hsrv1_ethhdr_sp *hsr_V1_hdr; struct hsr_sup_tlv *hsr_sup_tlv;
u16 total_length = 0;
/* Get next tlv */
total_length += hsr_sup_tag->tlv.HSR_TLV_length; if (!pskb_may_pull(skb, total_length)) returnfalse;
skb_pull(skb, total_length);
hsr_sup_tlv = (struct hsr_sup_tlv *)skb->data;
skb_push(skb, total_length);
/* if this is a redbox supervision frame we need to verify * that more data is available
*/ if (hsr_sup_tlv->HSR_TLV_type == PRP_TLV_REDBOX_MAC) { /* tlv length must be a length of a mac address */ if (hsr_sup_tlv->HSR_TLV_length != sizeof(struct hsr_sup_payload)) returnfalse;
/* make sure another tlv follows */
total_length += sizeof(struct hsr_sup_tlv) + hsr_sup_tlv->HSR_TLV_length; if (!pskb_may_pull(skb, total_length)) returnfalse;
/* get next tlv */
skb_pull(skb, total_length);
hsr_sup_tlv = (struct hsr_sup_tlv *)skb->data;
skb_push(skb, total_length);
}
/* end of tlvs must follow at the end */ if (hsr_sup_tlv->HSR_TLV_type == HSR_TLV_EOT &&
hsr_sup_tlv->HSR_TLV_length != 0) returnfalse;
/* For RedBox (HSR-SAN) check if we have received the supervision * frame with MAC addresses from own ProxyNodeTable.
*/ return hsr_is_node_in_db(&hsr->proxy_node_db,
payload->macaddress_A);
}
/* Add net_id in the upper 3 bits of lane_id */
lane_id |= port->hsr->net_id;
set_prp_lan_id(trailer, lane_id);
}
/* Tailroom for PRP rct should have been created before calling this */ staticstruct sk_buff *prp_fill_rct(struct sk_buff *skb, struct hsr_frame_info *frame, struct hsr_port *port)
{ struct prp_rct *trailer; int min_size = ETH_ZLEN; int lsdu_size;
/* pad to minimum packet size which is 60 + 6 (HSR tag) */ if (skb_put_padto(skb, ETH_ZLEN + HSR_HLEN)) return NULL;
lsdu_size = skb->len - 14; if (frame->is_vlan)
lsdu_size -= 4;
pc = skb_mac_header(skb); if (frame->is_vlan) /* This 4-byte shift (size of a vlan tag) does not * mean that the ethhdr starts there. But rather it * provides the proper environment for accessing * the fields, such as hsr_tag etc., just like * when the vlan tag is not there. This is because * the hsr tag is after the vlan tag.
*/
hsr_ethhdr = (struct hsr_ethhdr *)(pc + VLAN_HLEN); else
hsr_ethhdr = (struct hsr_ethhdr *)pc;
/* If the original frame was an HSR tagged frame, just clone it to be sent * unchanged. Otherwise, create a private frame especially tagged for 'port'.
*/ struct sk_buff *hsr_create_tagged_frame(struct hsr_frame_info *frame, struct hsr_port *port)
{ unsignedchar *dst, *src; struct sk_buff *skb; int movelen;
if (frame->skb_hsr) { struct hsr_ethhdr *hsr_ethhdr =
(struct hsr_ethhdr *)skb_mac_header(frame->skb_hsr);
/* set the lane id properly */
hsr_set_path_id(frame, hsr_ethhdr, port); return skb_clone(frame->skb_hsr, GFP_ATOMIC);
} elseif (port->dev->features & NETIF_F_HW_HSR_TAG_INS) { return skb_clone(frame->skb_std, GFP_ATOMIC);
}
/* Create the new skb with enough headroom to fit the HSR tag */
skb = __pskb_copy(frame->skb_std,
skb_headroom(frame->skb_std) + HSR_HLEN, GFP_ATOMIC); if (!skb) return NULL;
skb_reset_mac_header(skb);
if (skb->ip_summed == CHECKSUM_PARTIAL)
skb->csum_start += HSR_HLEN;
movelen = ETH_HLEN; if (frame->is_vlan)
movelen += VLAN_HLEN;
/* Address substitution (IEC62439-3 pp 26, 50): replace mac * address of outgoing frame with that of the outgoing slave's.
*/
ether_addr_copy(eth_hdr(skb)->h_source, port->dev->dev_addr);
}
/* When HSR node is used as RedBox - the frame received from HSR ring * requires source MAC address (SA) replacement to one which can be * recognized by SAN devices (otherwise, frames are dropped by switch)
*/ if (port->type == HSR_PT_INTERLINK)
ether_addr_copy(eth_hdr(skb)->h_source,
port->hsr->macaddress_redbox);
if (port->dev->features & NETIF_F_HW_HSR_FWD) return prp_drop_frame(frame, port);
/* RedBox specific frames dropping policies * * Do not send HSR supervisory frames to SAN devices
*/ if (frame->is_supervision && port->type == HSR_PT_INTERLINK) returntrue;
/* Do not forward to other HSR port (A or B) unicast frames which * are addressed to interlink port (and are in the ProxyNodeTable).
*/
skb = frame->skb_hsr; if (skb && prp_drop_frame(frame, port) &&
is_unicast_ether_addr(eth_hdr(skb)->h_dest) &&
hsr_is_node_in_db(&port->hsr->proxy_node_db,
eth_hdr(skb)->h_dest)) { returntrue;
}
/* Do not forward to port C (Interlink) frames from nodes A and B * if DA is in NodeTable.
*/ if ((frame->port_rcv->type == HSR_PT_SLAVE_A ||
frame->port_rcv->type == HSR_PT_SLAVE_B) &&
port->type == HSR_PT_INTERLINK) {
skb = frame->skb_hsr; if (skb && is_unicast_ether_addr(eth_hdr(skb)->h_dest) &&
hsr_is_node_in_db(&port->hsr->node_db,
eth_hdr(skb)->h_dest)) { returntrue;
}
}
/* Do not forward to port A and B unicast frames received on the * interlink port if it is addressed to one of nodes registered in * the ProxyNodeTable.
*/ if ((port->type == HSR_PT_SLAVE_A || port->type == HSR_PT_SLAVE_B) &&
frame->port_rcv->type == HSR_PT_INTERLINK) {
skb = frame->skb_std; if (skb && is_unicast_ether_addr(eth_hdr(skb)->h_dest) &&
hsr_is_node_in_db(&port->hsr->proxy_node_db,
eth_hdr(skb)->h_dest)) { returntrue;
}
}
returnfalse;
}
/* Forward the frame through all devices except: * - Back through the receiving device * - If it's a HSR frame: through a device where it has passed before * - if it's a PRP frame: through another PRP slave device (no bridge) * - To the local HSR master only if the frame is directly addressed to it, or * a non-supervision multicast or broadcast frame. * * HSR slave devices should insert a HSR tag into the frame, or forward the * frame unchanged if it's already tagged. Interlink devices should strip HSR * tags if they're of the non-HSR type (but only after duplicate discard). The * master device always strips HSR tags.
*/ staticvoid hsr_forward_do(struct hsr_frame_info *frame)
{ struct hsr_port *port; struct sk_buff *skb; bool sent = false;
hsr_for_each_port(frame->port_rcv->hsr, port) { struct hsr_priv *hsr = port->hsr; /* Don't send frame back the way it came */ if (port == frame->port_rcv) continue;
/* Don't deliver locally unless we should */ if (port->type == HSR_PT_MASTER && !frame->is_local_dest) continue;
/* Deliver frames directly addressed to us to master only */ if (port->type != HSR_PT_MASTER && frame->is_local_exclusive) continue;
/* If hardware duplicate generation is enabled, only send out * one port.
*/ if ((port->dev->features & NETIF_F_HW_HSR_DUP) && sent) continue;
/* Don't send frame over port where it has been sent before. * Also for SAN, this shouldn't be done.
*/ if (!frame->is_from_san &&
hsr->proto_ops->register_frame_out &&
hsr->proto_ops->register_frame_out(port, frame)) continue;
/* Check if frame is to be dropped. Eg. for PRP no forward * between ports, or sending HSR supervision to RedBox.
*/ if (hsr->proto_ops->drop_frame &&
hsr->proto_ops->drop_frame(frame, port)) continue;
if (port->type != HSR_PT_MASTER)
frame->is_from_san = true;
if (port->type == HSR_PT_MASTER ||
port->type == HSR_PT_INTERLINK) { /* Sequence nr for the master/interlink node */
lockdep_assert_held(&hsr->seqnr_lock);
frame->sequence_nr = hsr->sequence_nr;
hsr->sequence_nr++;
}
}
int hsr_fill_frame_info(__be16 proto, struct sk_buff *skb, struct hsr_frame_info *frame)
{ struct hsr_port *port = frame->port_rcv; struct hsr_priv *hsr = port->hsr;
/* HSRv0 supervisory frames double as a tag so treat them as tagged. */ if ((!hsr->prot_version && proto == htons(ETH_P_PRP)) ||
proto == htons(ETH_P_HSR)) { /* Check if skb contains hsr_ethhdr */ if (skb->mac_len < sizeof(struct hsr_ethhdr)) return -EINVAL;
n_db = &hsr->node_db; if (port->type == HSR_PT_INTERLINK)
n_db = &hsr->proxy_node_db;
frame->node_src = hsr_get_node(port, n_db, skb,
frame->is_supervision, port->type); if (!frame->node_src) return -1; /* Unknown node and !is_supervision, or no mem */
if (proto == htons(ETH_P_8021Q))
frame->is_vlan = true;
if (frame->is_vlan) { /* Note: skb->mac_len might be wrong here. */ if (!pskb_may_pull(skb,
skb_mac_offset(skb) +
offsetofend(struct hsr_vlan_ethhdr, vlanhdr))) return -EINVAL;
vlan_hdr = (struct hsr_vlan_ethhdr *)skb_mac_header(skb);
proto = vlan_hdr->vlanhdr.h_vlan_encapsulated_proto;
}
frame->is_from_san = false;
frame->port_rcv = port;
ret = hsr->proto_ops->fill_frame_info(proto, skb, frame); if (ret) return ret;
check_local_dest(port->hsr, skb, frame);
return 0;
}
/* Must be called holding rcu read lock (because of the port parameter) */ void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port)
{ struct hsr_frame_info frame;
rcu_read_lock(); if (fill_frame_info(&frame, skb, port) < 0) goto out_drop;
hsr_register_frame_in(frame.node_src, port, frame.sequence_nr);
hsr_forward_do(&frame);
rcu_read_unlock(); /* Gets called for ingress frames as well as egress from master port. * So check and increment stats for master port only here.
*/ if (port->type == HSR_PT_MASTER || port->type == HSR_PT_INTERLINK) {
port->dev->stats.tx_packets++;
port->dev->stats.tx_bytes += skb->len;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.