vlan_dev.c
来自「linux 内核源代码」· C语言 代码 · 共 734 行 · 第 1/2 页
C
734 行
if (!(VLAN_DEV_INFO(dev)->flags & VLAN_FLAG_REORDER_HDR)) build_vlan_header = 1; if (build_vlan_header) { vhdr = (struct vlan_hdr *) skb_push(skb, VLAN_HLEN); /* build the four bytes that make this a VLAN header. */ /* Now, construct the second two bytes. This field looks something * like: * usr_priority: 3 bits (high bits) * CFI 1 bit * VLAN ID 12 bits (low bits) * */ veth_TCI = VLAN_DEV_INFO(dev)->vlan_id; veth_TCI |= vlan_dev_get_egress_qos_mask(dev, skb); vhdr->h_vlan_TCI = htons(veth_TCI); /* * Set the protocol type. * For a packet of type ETH_P_802_3 we put the length in here instead. * It is up to the 802.2 layer to carry protocol information. */ if (type != ETH_P_802_3) { vhdr->h_vlan_encapsulated_proto = htons(type); } else { vhdr->h_vlan_encapsulated_proto = htons(len); } skb->protocol = htons(ETH_P_8021Q); skb_reset_network_header(skb); } /* Before delegating work to the lower layer, enter our MAC-address */ if (saddr == NULL) saddr = dev->dev_addr; dev = VLAN_DEV_INFO(dev)->real_dev; /* MPLS can send us skbuffs w/out enough space. This check will grow the * skb if it doesn't have enough headroom. Not a beautiful solution, so * I'll tick a counter so that users can know it's happening... If they * care... */ /* NOTE: This may still break if the underlying device is not the final * device (and thus there are more headers to add...) It should work for * good-ole-ethernet though. */ if (skb_headroom(skb) < dev->hard_header_len) { struct sk_buff *sk_tmp = skb; skb = skb_realloc_headroom(sk_tmp, dev->hard_header_len); kfree_skb(sk_tmp); if (skb == NULL) { struct net_device_stats *stats = vlan_dev_get_stats(vdev); stats->tx_dropped++; return -ENOMEM; } VLAN_DEV_INFO(vdev)->cnt_inc_headroom_on_tx++;#ifdef VLAN_DEBUG printk(VLAN_DBG "%s: %s: had to grow skb.\n", __FUNCTION__, vdev->name);#endif } if (build_vlan_header) { /* Now make the underlying real hard header */ rc = dev_hard_header(skb, dev, ETH_P_8021Q, daddr, saddr, len + VLAN_HLEN); if (rc > 0) rc += VLAN_HLEN; else if (rc < 0) rc -= VLAN_HLEN; } else /* If here, then we'll just make a normal looking ethernet frame, * but, the hard_start_xmit method will insert the tag (it has to * be able to do this for bridged and other skbs that don't come * down the protocol stack in an orderly manner. */ rc = dev_hard_header(skb, dev, type, daddr, saddr, len); return rc;}int vlan_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev){ struct net_device_stats *stats = vlan_dev_get_stats(dev); struct vlan_ethhdr *veth = (struct vlan_ethhdr *)(skb->data); /* Handle non-VLAN frames if they are sent to us, for example by DHCP. * * NOTE: THIS ASSUMES DIX ETHERNET, SPECIFICALLY NOT SUPPORTING * OTHER THINGS LIKE FDDI/TokenRing/802.3 SNAPs... */ if (veth->h_vlan_proto != htons(ETH_P_8021Q) || VLAN_DEV_INFO(dev)->flags & VLAN_FLAG_REORDER_HDR) { int orig_headroom = skb_headroom(skb); unsigned short veth_TCI; /* This is not a VLAN frame...but we can fix that! */ VLAN_DEV_INFO(dev)->cnt_encap_on_xmit++;#ifdef VLAN_DEBUG printk(VLAN_DBG "%s: proto to encap: 0x%hx (hbo)\n", __FUNCTION__, htons(veth->h_vlan_proto));#endif /* Construct the second two bytes. This field looks something * like: * usr_priority: 3 bits (high bits) * CFI 1 bit * VLAN ID 12 bits (low bits) */ veth_TCI = VLAN_DEV_INFO(dev)->vlan_id; veth_TCI |= vlan_dev_get_egress_qos_mask(dev, skb); skb = __vlan_put_tag(skb, veth_TCI); if (!skb) { stats->tx_dropped++; return 0; } if (orig_headroom < VLAN_HLEN) { VLAN_DEV_INFO(dev)->cnt_inc_headroom_on_tx++; } }#ifdef VLAN_DEBUG printk(VLAN_DBG "%s: about to send skb: %p to dev: %s\n", __FUNCTION__, skb, skb->dev->name); printk(VLAN_DBG " %2hx.%2hx.%2hx.%2xh.%2hx.%2hx %2hx.%2hx.%2hx.%2hx.%2hx.%2hx %4hx %4hx %4hx\n", veth->h_dest[0], veth->h_dest[1], veth->h_dest[2], veth->h_dest[3], veth->h_dest[4], veth->h_dest[5], veth->h_source[0], veth->h_source[1], veth->h_source[2], veth->h_source[3], veth->h_source[4], veth->h_source[5], veth->h_vlan_proto, veth->h_vlan_TCI, veth->h_vlan_encapsulated_proto);#endif stats->tx_packets++; /* for statics only */ stats->tx_bytes += skb->len; skb->dev = VLAN_DEV_INFO(dev)->real_dev; dev_queue_xmit(skb); return 0;}int vlan_dev_hwaccel_hard_start_xmit(struct sk_buff *skb, struct net_device *dev){ struct net_device_stats *stats = vlan_dev_get_stats(dev); unsigned short veth_TCI; /* Construct the second two bytes. This field looks something * like: * usr_priority: 3 bits (high bits) * CFI 1 bit * VLAN ID 12 bits (low bits) */ veth_TCI = VLAN_DEV_INFO(dev)->vlan_id; veth_TCI |= vlan_dev_get_egress_qos_mask(dev, skb); skb = __vlan_hwaccel_put_tag(skb, veth_TCI); stats->tx_packets++; stats->tx_bytes += skb->len; skb->dev = VLAN_DEV_INFO(dev)->real_dev; dev_queue_xmit(skb); return 0;}int vlan_dev_change_mtu(struct net_device *dev, int new_mtu){ /* TODO: gotta make sure the underlying layer can handle it, * maybe an IFF_VLAN_CAPABLE flag for devices? */ if (VLAN_DEV_INFO(dev)->real_dev->mtu < new_mtu) return -ERANGE; dev->mtu = new_mtu; return 0;}void vlan_dev_set_ingress_priority(const struct net_device *dev, u32 skb_prio, short vlan_prio){ struct vlan_dev_info *vlan = VLAN_DEV_INFO(dev); if (vlan->ingress_priority_map[vlan_prio & 0x7] && !skb_prio) vlan->nr_ingress_mappings--; else if (!vlan->ingress_priority_map[vlan_prio & 0x7] && skb_prio) vlan->nr_ingress_mappings++; vlan->ingress_priority_map[vlan_prio & 0x7] = skb_prio;}int vlan_dev_set_egress_priority(const struct net_device *dev, u32 skb_prio, short vlan_prio){ struct vlan_dev_info *vlan = VLAN_DEV_INFO(dev); struct vlan_priority_tci_mapping *mp = NULL; struct vlan_priority_tci_mapping *np; u32 vlan_qos = (vlan_prio << 13) & 0xE000; /* See if a priority mapping exists.. */ mp = vlan->egress_priority_map[skb_prio & 0xF]; while (mp) { if (mp->priority == skb_prio) { if (mp->vlan_qos && !vlan_qos) vlan->nr_egress_mappings--; else if (!mp->vlan_qos && vlan_qos) vlan->nr_egress_mappings++; mp->vlan_qos = vlan_qos; return 0; } mp = mp->next; } /* Create a new mapping then. */ mp = vlan->egress_priority_map[skb_prio & 0xF]; np = kmalloc(sizeof(struct vlan_priority_tci_mapping), GFP_KERNEL); if (!np) return -ENOBUFS; np->next = mp; np->priority = skb_prio; np->vlan_qos = vlan_qos; vlan->egress_priority_map[skb_prio & 0xF] = np; if (vlan_qos) vlan->nr_egress_mappings++; return 0;}/* Flags are defined in the vlan_flags enum in include/linux/if_vlan.h file. */int vlan_dev_set_vlan_flag(const struct net_device *dev, u32 flag, short flag_val){ /* verify flag is supported */ if (flag == VLAN_FLAG_REORDER_HDR) { if (flag_val) { VLAN_DEV_INFO(dev)->flags |= VLAN_FLAG_REORDER_HDR; } else { VLAN_DEV_INFO(dev)->flags &= ~VLAN_FLAG_REORDER_HDR; } return 0; } printk(KERN_ERR "%s: flag %i is not valid.\n", __FUNCTION__, flag); return -EINVAL;}void vlan_dev_get_realdev_name(const struct net_device *dev, char *result){ strncpy(result, VLAN_DEV_INFO(dev)->real_dev->name, 23);}void vlan_dev_get_vid(const struct net_device *dev, unsigned short *result){ *result = VLAN_DEV_INFO(dev)->vlan_id;}int vlan_dev_open(struct net_device *dev){ struct vlan_dev_info *vlan = VLAN_DEV_INFO(dev); struct net_device *real_dev = vlan->real_dev; int err; if (!(real_dev->flags & IFF_UP)) return -ENETDOWN; if (compare_ether_addr(dev->dev_addr, real_dev->dev_addr)) { err = dev_unicast_add(real_dev, dev->dev_addr, ETH_ALEN); if (err < 0) return err; } memcpy(vlan->real_dev_addr, real_dev->dev_addr, ETH_ALEN); if (dev->flags & IFF_ALLMULTI) dev_set_allmulti(real_dev, 1); if (dev->flags & IFF_PROMISC) dev_set_promiscuity(real_dev, 1); return 0;}int vlan_dev_stop(struct net_device *dev){ struct net_device *real_dev = VLAN_DEV_INFO(dev)->real_dev; dev_mc_unsync(real_dev, dev); if (dev->flags & IFF_ALLMULTI) dev_set_allmulti(real_dev, -1); if (dev->flags & IFF_PROMISC) dev_set_promiscuity(real_dev, -1); if (compare_ether_addr(dev->dev_addr, real_dev->dev_addr)) dev_unicast_delete(real_dev, dev->dev_addr, dev->addr_len); return 0;}int vlan_set_mac_address(struct net_device *dev, void *p){ struct net_device *real_dev = VLAN_DEV_INFO(dev)->real_dev; struct sockaddr *addr = p; int err; if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; if (!(dev->flags & IFF_UP)) goto out; if (compare_ether_addr(addr->sa_data, real_dev->dev_addr)) { err = dev_unicast_add(real_dev, addr->sa_data, ETH_ALEN); if (err < 0) return err; } if (compare_ether_addr(dev->dev_addr, real_dev->dev_addr)) dev_unicast_delete(real_dev, dev->dev_addr, ETH_ALEN);out: memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); return 0;}int vlan_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd){ struct net_device *real_dev = VLAN_DEV_INFO(dev)->real_dev; struct ifreq ifrr; int err = -EOPNOTSUPP; strncpy(ifrr.ifr_name, real_dev->name, IFNAMSIZ); ifrr.ifr_ifru = ifr->ifr_ifru; switch(cmd) { case SIOCGMIIPHY: case SIOCGMIIREG: case SIOCSMIIREG: if (real_dev->do_ioctl && netif_device_present(real_dev)) err = real_dev->do_ioctl(real_dev, &ifrr, cmd); break; } if (!err) ifr->ifr_ifru = ifrr.ifr_ifru; return err;}void vlan_change_rx_flags(struct net_device *dev, int change){ struct net_device *real_dev = VLAN_DEV_INFO(dev)->real_dev; if (change & IFF_ALLMULTI) dev_set_allmulti(real_dev, dev->flags & IFF_ALLMULTI ? 1 : -1); if (change & IFF_PROMISC) dev_set_promiscuity(real_dev, dev->flags & IFF_PROMISC ? 1 : -1);}/** Taken from Gleb + Lennert's VLAN code, and modified... */void vlan_dev_set_multicast_list(struct net_device *vlan_dev){ dev_mc_sync(VLAN_DEV_INFO(vlan_dev)->real_dev, vlan_dev);}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?