diff options
-rw-r--r-- | include/net/protocol.h | 3 | ||||
-rw-r--r-- | net/ipv4/af_inet.c | 97 |
2 files changed, 100 insertions, 0 deletions
diff --git a/include/net/protocol.h b/include/net/protocol.h index 8d024d7cb741..cb2965aa1b62 100644 --- a/include/net/protocol.h +++ b/include/net/protocol.h | |||
@@ -39,6 +39,9 @@ struct net_protocol { | |||
39 | int (*gso_send_check)(struct sk_buff *skb); | 39 | int (*gso_send_check)(struct sk_buff *skb); |
40 | struct sk_buff *(*gso_segment)(struct sk_buff *skb, | 40 | struct sk_buff *(*gso_segment)(struct sk_buff *skb, |
41 | int features); | 41 | int features); |
42 | struct sk_buff **(*gro_receive)(struct sk_buff **head, | ||
43 | struct sk_buff *skb); | ||
44 | int (*gro_complete)(struct sk_buff *skb); | ||
42 | unsigned int no_policy:1, | 45 | unsigned int no_policy:1, |
43 | netns_ok:1; | 46 | netns_ok:1; |
44 | }; | 47 | }; |
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index fe03048c130d..a85595307fa7 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c | |||
@@ -94,6 +94,7 @@ | |||
94 | #include <linux/igmp.h> | 94 | #include <linux/igmp.h> |
95 | #include <linux/inetdevice.h> | 95 | #include <linux/inetdevice.h> |
96 | #include <linux/netdevice.h> | 96 | #include <linux/netdevice.h> |
97 | #include <net/checksum.h> | ||
97 | #include <net/ip.h> | 98 | #include <net/ip.h> |
98 | #include <net/protocol.h> | 99 | #include <net/protocol.h> |
99 | #include <net/arp.h> | 100 | #include <net/arp.h> |
@@ -1241,6 +1242,100 @@ out: | |||
1241 | return segs; | 1242 | return segs; |
1242 | } | 1243 | } |
1243 | 1244 | ||
1245 | static struct sk_buff **inet_gro_receive(struct sk_buff **head, | ||
1246 | struct sk_buff *skb) | ||
1247 | { | ||
1248 | struct net_protocol *ops; | ||
1249 | struct sk_buff **pp = NULL; | ||
1250 | struct sk_buff *p; | ||
1251 | struct iphdr *iph; | ||
1252 | int flush = 1; | ||
1253 | int proto; | ||
1254 | int id; | ||
1255 | |||
1256 | if (unlikely(!pskb_may_pull(skb, sizeof(*iph)))) | ||
1257 | goto out; | ||
1258 | |||
1259 | iph = ip_hdr(skb); | ||
1260 | proto = iph->protocol & (MAX_INET_PROTOS - 1); | ||
1261 | |||
1262 | rcu_read_lock(); | ||
1263 | ops = rcu_dereference(inet_protos[proto]); | ||
1264 | if (!ops || !ops->gro_receive) | ||
1265 | goto out_unlock; | ||
1266 | |||
1267 | if (iph->version != 4 || iph->ihl != 5) | ||
1268 | goto out_unlock; | ||
1269 | |||
1270 | if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl))) | ||
1271 | goto out_unlock; | ||
1272 | |||
1273 | flush = ntohs(iph->tot_len) != skb->len || | ||
1274 | iph->frag_off != htons(IP_DF); | ||
1275 | id = ntohs(iph->id); | ||
1276 | |||
1277 | for (p = *head; p; p = p->next) { | ||
1278 | struct iphdr *iph2; | ||
1279 | |||
1280 | if (!NAPI_GRO_CB(p)->same_flow) | ||
1281 | continue; | ||
1282 | |||
1283 | iph2 = ip_hdr(p); | ||
1284 | |||
1285 | if (iph->protocol != iph2->protocol || | ||
1286 | iph->tos != iph2->tos || | ||
1287 | memcmp(&iph->saddr, &iph2->saddr, 8)) { | ||
1288 | NAPI_GRO_CB(p)->same_flow = 0; | ||
1289 | continue; | ||
1290 | } | ||
1291 | |||
1292 | /* All fields must match except length and checksum. */ | ||
1293 | NAPI_GRO_CB(p)->flush |= | ||
1294 | memcmp(&iph->frag_off, &iph2->frag_off, 4) || | ||
1295 | (u16)(ntohs(iph2->id) + NAPI_GRO_CB(p)->count) != id; | ||
1296 | |||
1297 | NAPI_GRO_CB(p)->flush |= flush; | ||
1298 | } | ||
1299 | |||
1300 | NAPI_GRO_CB(skb)->flush |= flush; | ||
1301 | __skb_pull(skb, sizeof(*iph)); | ||
1302 | skb_reset_transport_header(skb); | ||
1303 | |||
1304 | pp = ops->gro_receive(head, skb); | ||
1305 | |||
1306 | out_unlock: | ||
1307 | rcu_read_unlock(); | ||
1308 | |||
1309 | out: | ||
1310 | NAPI_GRO_CB(skb)->flush |= flush; | ||
1311 | |||
1312 | return pp; | ||
1313 | } | ||
1314 | |||
1315 | static int inet_gro_complete(struct sk_buff *skb) | ||
1316 | { | ||
1317 | struct net_protocol *ops; | ||
1318 | struct iphdr *iph = ip_hdr(skb); | ||
1319 | int proto = iph->protocol & (MAX_INET_PROTOS - 1); | ||
1320 | int err = -ENOSYS; | ||
1321 | __be16 newlen = htons(skb->len - skb_network_offset(skb)); | ||
1322 | |||
1323 | csum_replace2(&iph->check, iph->tot_len, newlen); | ||
1324 | iph->tot_len = newlen; | ||
1325 | |||
1326 | rcu_read_lock(); | ||
1327 | ops = rcu_dereference(inet_protos[proto]); | ||
1328 | if (WARN_ON(!ops || !ops->gro_complete)) | ||
1329 | goto out_unlock; | ||
1330 | |||
1331 | err = ops->gro_complete(skb); | ||
1332 | |||
1333 | out_unlock: | ||
1334 | rcu_read_unlock(); | ||
1335 | |||
1336 | return err; | ||
1337 | } | ||
1338 | |||
1244 | int inet_ctl_sock_create(struct sock **sk, unsigned short family, | 1339 | int inet_ctl_sock_create(struct sock **sk, unsigned short family, |
1245 | unsigned short type, unsigned char protocol, | 1340 | unsigned short type, unsigned char protocol, |
1246 | struct net *net) | 1341 | struct net *net) |
@@ -1407,6 +1502,8 @@ static struct packet_type ip_packet_type = { | |||
1407 | .func = ip_rcv, | 1502 | .func = ip_rcv, |
1408 | .gso_send_check = inet_gso_send_check, | 1503 | .gso_send_check = inet_gso_send_check, |
1409 | .gso_segment = inet_gso_segment, | 1504 | .gso_segment = inet_gso_segment, |
1505 | .gro_receive = inet_gro_receive, | ||
1506 | .gro_complete = inet_gro_complete, | ||
1410 | }; | 1507 | }; |
1411 | 1508 | ||
1412 | static int __init inet_init(void) | 1509 | static int __init inet_init(void) |