|
@@ -618,6 +618,92 @@ out:
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+static struct sk_buff **vlan_gro_receive(struct sk_buff **head,
|
|
|
+ struct sk_buff *skb)
|
|
|
+{
|
|
|
+ struct sk_buff *p, **pp = NULL;
|
|
|
+ struct vlan_hdr *vhdr;
|
|
|
+ unsigned int hlen, off_vlan;
|
|
|
+ const struct packet_offload *ptype;
|
|
|
+ __be16 type;
|
|
|
+ int flush = 1;
|
|
|
+
|
|
|
+ off_vlan = skb_gro_offset(skb);
|
|
|
+ hlen = off_vlan + sizeof(*vhdr);
|
|
|
+ vhdr = skb_gro_header_fast(skb, off_vlan);
|
|
|
+ if (skb_gro_header_hard(skb, hlen)) {
|
|
|
+ vhdr = skb_gro_header_slow(skb, hlen, off_vlan);
|
|
|
+ if (unlikely(!vhdr))
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ type = vhdr->h_vlan_encapsulated_proto;
|
|
|
+
|
|
|
+ rcu_read_lock();
|
|
|
+ ptype = gro_find_receive_by_type(type);
|
|
|
+ if (!ptype)
|
|
|
+ goto out_unlock;
|
|
|
+
|
|
|
+ flush = 0;
|
|
|
+
|
|
|
+ for (p = *head; p; p = p->next) {
|
|
|
+ struct vlan_hdr *vhdr2;
|
|
|
+
|
|
|
+ if (!NAPI_GRO_CB(p)->same_flow)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ vhdr2 = (struct vlan_hdr *)(p->data + off_vlan);
|
|
|
+ if (compare_vlan_header(vhdr, vhdr2))
|
|
|
+ NAPI_GRO_CB(p)->same_flow = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ skb_gro_pull(skb, sizeof(*vhdr));
|
|
|
+ skb_gro_postpull_rcsum(skb, vhdr, sizeof(*vhdr));
|
|
|
+ pp = ptype->callbacks.gro_receive(head, skb);
|
|
|
+
|
|
|
+out_unlock:
|
|
|
+ rcu_read_unlock();
|
|
|
+out:
|
|
|
+ NAPI_GRO_CB(skb)->flush |= flush;
|
|
|
+
|
|
|
+ return pp;
|
|
|
+}
|
|
|
+
|
|
|
+static int vlan_gro_complete(struct sk_buff *skb, int nhoff)
|
|
|
+{
|
|
|
+ struct vlan_hdr *vhdr = (struct vlan_hdr *)(skb->data + nhoff);
|
|
|
+ __be16 type = vhdr->h_vlan_encapsulated_proto;
|
|
|
+ struct packet_offload *ptype;
|
|
|
+ int err = -ENOENT;
|
|
|
+
|
|
|
+ rcu_read_lock();
|
|
|
+ ptype = gro_find_complete_by_type(type);
|
|
|
+ if (ptype)
|
|
|
+ err = ptype->callbacks.gro_complete(skb, nhoff + sizeof(*vhdr));
|
|
|
+
|
|
|
+ rcu_read_unlock();
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static struct packet_offload vlan_packet_offloads[] __read_mostly = {
|
|
|
+ {
|
|
|
+ .type = cpu_to_be16(ETH_P_8021Q),
|
|
|
+ .priority = 10,
|
|
|
+ .callbacks = {
|
|
|
+ .gro_receive = vlan_gro_receive,
|
|
|
+ .gro_complete = vlan_gro_complete,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .type = cpu_to_be16(ETH_P_8021AD),
|
|
|
+ .priority = 10,
|
|
|
+ .callbacks = {
|
|
|
+ .gro_receive = vlan_gro_receive,
|
|
|
+ .gro_complete = vlan_gro_complete,
|
|
|
+ },
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
static int __net_init vlan_init_net(struct net *net)
|
|
|
{
|
|
|
struct vlan_net *vn = net_generic(net, vlan_net_id);
|
|
@@ -645,6 +731,7 @@ static struct pernet_operations vlan_net_ops = {
|
|
|
static int __init vlan_proto_init(void)
|
|
|
{
|
|
|
int err;
|
|
|
+ unsigned int i;
|
|
|
|
|
|
pr_info("%s v%s\n", vlan_fullname, vlan_version);
|
|
|
|
|
@@ -668,6 +755,9 @@ static int __init vlan_proto_init(void)
|
|
|
if (err < 0)
|
|
|
goto err5;
|
|
|
|
|
|
+ for (i = 0; i < ARRAY_SIZE(vlan_packet_offloads); i++)
|
|
|
+ dev_add_offload(&vlan_packet_offloads[i]);
|
|
|
+
|
|
|
vlan_ioctl_set(vlan_ioctl_handler);
|
|
|
return 0;
|
|
|
|
|
@@ -685,7 +775,13 @@ err0:
|
|
|
|
|
|
static void __exit vlan_cleanup_module(void)
|
|
|
{
|
|
|
+ unsigned int i;
|
|
|
+
|
|
|
vlan_ioctl_set(NULL);
|
|
|
+
|
|
|
+ for (i = 0; i < ARRAY_SIZE(vlan_packet_offloads); i++)
|
|
|
+ dev_remove_offload(&vlan_packet_offloads[i]);
|
|
|
+
|
|
|
vlan_netlink_fini();
|
|
|
|
|
|
unregister_netdevice_notifier(&vlan_notifier_block);
|