diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2011-10-06 06:28:31 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-10-18 23:22:07 -0400 |
commit | bc416d9768aa9a2e46eb11354a9c58399dafeb01 (patch) | |
tree | a3ecf948353d11d199dfb64b0c4302c5b4aaccb6 /net | |
parent | f7ba35da583cf6aefba887a8dcf86acec4f3a350 (diff) |
macvlan: handle fragmented multicast frames
Fragmented multicast frames are delivered to a single macvlan port,
because ip defrag logic considers other samples are redundant.
Implement a defrag step before trying to send the multicast frame.
Reported-by: Ben Greear <greearb@candelatech.com>
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/ip_fragment.c | 36 | ||||
-rw-r--r-- | net/packet/af_packet.c | 39 |
2 files changed, 37 insertions, 38 deletions
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 0e0ab98abc6f..763589ad673d 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c | |||
@@ -682,6 +682,42 @@ int ip_defrag(struct sk_buff *skb, u32 user) | |||
682 | } | 682 | } |
683 | EXPORT_SYMBOL(ip_defrag); | 683 | EXPORT_SYMBOL(ip_defrag); |
684 | 684 | ||
685 | struct sk_buff *ip_check_defrag(struct sk_buff *skb, u32 user) | ||
686 | { | ||
687 | const struct iphdr *iph; | ||
688 | u32 len; | ||
689 | |||
690 | if (skb->protocol != htons(ETH_P_IP)) | ||
691 | return skb; | ||
692 | |||
693 | if (!pskb_may_pull(skb, sizeof(struct iphdr))) | ||
694 | return skb; | ||
695 | |||
696 | iph = ip_hdr(skb); | ||
697 | if (iph->ihl < 5 || iph->version != 4) | ||
698 | return skb; | ||
699 | if (!pskb_may_pull(skb, iph->ihl*4)) | ||
700 | return skb; | ||
701 | iph = ip_hdr(skb); | ||
702 | len = ntohs(iph->tot_len); | ||
703 | if (skb->len < len || len < (iph->ihl * 4)) | ||
704 | return skb; | ||
705 | |||
706 | if (ip_is_fragment(ip_hdr(skb))) { | ||
707 | skb = skb_share_check(skb, GFP_ATOMIC); | ||
708 | if (skb) { | ||
709 | if (pskb_trim_rcsum(skb, len)) | ||
710 | return skb; | ||
711 | memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); | ||
712 | if (ip_defrag(skb, user)) | ||
713 | return NULL; | ||
714 | skb->rxhash = 0; | ||
715 | } | ||
716 | } | ||
717 | return skb; | ||
718 | } | ||
719 | EXPORT_SYMBOL(ip_check_defrag); | ||
720 | |||
685 | #ifdef CONFIG_SYSCTL | 721 | #ifdef CONFIG_SYSCTL |
686 | static int zero; | 722 | static int zero; |
687 | 723 | ||
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 7b5f03253016..03bb45adf2fc 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c | |||
@@ -1213,43 +1213,6 @@ static struct sock *fanout_demux_cpu(struct packet_fanout *f, struct sk_buff *sk | |||
1213 | return f->arr[cpu % num]; | 1213 | return f->arr[cpu % num]; |
1214 | } | 1214 | } |
1215 | 1215 | ||
1216 | static struct sk_buff *fanout_check_defrag(struct sk_buff *skb) | ||
1217 | { | ||
1218 | #ifdef CONFIG_INET | ||
1219 | const struct iphdr *iph; | ||
1220 | u32 len; | ||
1221 | |||
1222 | if (skb->protocol != htons(ETH_P_IP)) | ||
1223 | return skb; | ||
1224 | |||
1225 | if (!pskb_may_pull(skb, sizeof(struct iphdr))) | ||
1226 | return skb; | ||
1227 | |||
1228 | iph = ip_hdr(skb); | ||
1229 | if (iph->ihl < 5 || iph->version != 4) | ||
1230 | return skb; | ||
1231 | if (!pskb_may_pull(skb, iph->ihl*4)) | ||
1232 | return skb; | ||
1233 | iph = ip_hdr(skb); | ||
1234 | len = ntohs(iph->tot_len); | ||
1235 | if (skb->len < len || len < (iph->ihl * 4)) | ||
1236 | return skb; | ||
1237 | |||
1238 | if (ip_is_fragment(ip_hdr(skb))) { | ||
1239 | skb = skb_share_check(skb, GFP_ATOMIC); | ||
1240 | if (skb) { | ||
1241 | if (pskb_trim_rcsum(skb, len)) | ||
1242 | return skb; | ||
1243 | memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); | ||
1244 | if (ip_defrag(skb, IP_DEFRAG_AF_PACKET)) | ||
1245 | return NULL; | ||
1246 | skb->rxhash = 0; | ||
1247 | } | ||
1248 | } | ||
1249 | #endif | ||
1250 | return skb; | ||
1251 | } | ||
1252 | |||
1253 | static int packet_rcv_fanout(struct sk_buff *skb, struct net_device *dev, | 1216 | static int packet_rcv_fanout(struct sk_buff *skb, struct net_device *dev, |
1254 | struct packet_type *pt, struct net_device *orig_dev) | 1217 | struct packet_type *pt, struct net_device *orig_dev) |
1255 | { | 1218 | { |
@@ -1268,7 +1231,7 @@ static int packet_rcv_fanout(struct sk_buff *skb, struct net_device *dev, | |||
1268 | case PACKET_FANOUT_HASH: | 1231 | case PACKET_FANOUT_HASH: |
1269 | default: | 1232 | default: |
1270 | if (f->defrag) { | 1233 | if (f->defrag) { |
1271 | skb = fanout_check_defrag(skb); | 1234 | skb = ip_check_defrag(skb, IP_DEFRAG_AF_PACKET); |
1272 | if (!skb) | 1235 | if (!skb) |
1273 | return 0; | 1236 | return 0; |
1274 | } | 1237 | } |