diff options
Diffstat (limited to 'net/ipv4/udp.c')
-rw-r--r-- | net/ipv4/udp.c | 156 |
1 files changed, 111 insertions, 45 deletions
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 80e3812837ad..ebaaa7f973d7 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c | |||
@@ -110,11 +110,12 @@ struct udp_table udp_table; | |||
110 | EXPORT_SYMBOL(udp_table); | 110 | EXPORT_SYMBOL(udp_table); |
111 | 111 | ||
112 | int sysctl_udp_mem[3] __read_mostly; | 112 | int sysctl_udp_mem[3] __read_mostly; |
113 | int sysctl_udp_rmem_min __read_mostly; | ||
114 | int sysctl_udp_wmem_min __read_mostly; | ||
115 | |||
116 | EXPORT_SYMBOL(sysctl_udp_mem); | 113 | EXPORT_SYMBOL(sysctl_udp_mem); |
114 | |||
115 | int sysctl_udp_rmem_min __read_mostly; | ||
117 | EXPORT_SYMBOL(sysctl_udp_rmem_min); | 116 | EXPORT_SYMBOL(sysctl_udp_rmem_min); |
117 | |||
118 | int sysctl_udp_wmem_min __read_mostly; | ||
118 | EXPORT_SYMBOL(sysctl_udp_wmem_min); | 119 | EXPORT_SYMBOL(sysctl_udp_wmem_min); |
119 | 120 | ||
120 | atomic_t udp_memory_allocated; | 121 | atomic_t udp_memory_allocated; |
@@ -158,7 +159,7 @@ static int udp_lib_lport_inuse(struct net *net, __u16 num, | |||
158 | */ | 159 | */ |
159 | int udp_lib_get_port(struct sock *sk, unsigned short snum, | 160 | int udp_lib_get_port(struct sock *sk, unsigned short snum, |
160 | int (*saddr_comp)(const struct sock *sk1, | 161 | int (*saddr_comp)(const struct sock *sk1, |
161 | const struct sock *sk2 ) ) | 162 | const struct sock *sk2)) |
162 | { | 163 | { |
163 | struct udp_hslot *hslot; | 164 | struct udp_hslot *hslot; |
164 | struct udp_table *udptable = sk->sk_prot->h.udp_table; | 165 | struct udp_table *udptable = sk->sk_prot->h.udp_table; |
@@ -221,14 +222,15 @@ fail_unlock: | |||
221 | fail: | 222 | fail: |
222 | return error; | 223 | return error; |
223 | } | 224 | } |
225 | EXPORT_SYMBOL(udp_lib_get_port); | ||
224 | 226 | ||
225 | static int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2) | 227 | static int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2) |
226 | { | 228 | { |
227 | struct inet_sock *inet1 = inet_sk(sk1), *inet2 = inet_sk(sk2); | 229 | struct inet_sock *inet1 = inet_sk(sk1), *inet2 = inet_sk(sk2); |
228 | 230 | ||
229 | return ( !ipv6_only_sock(sk2) && | 231 | return (!ipv6_only_sock(sk2) && |
230 | (!inet1->rcv_saddr || !inet2->rcv_saddr || | 232 | (!inet1->rcv_saddr || !inet2->rcv_saddr || |
231 | inet1->rcv_saddr == inet2->rcv_saddr )); | 233 | inet1->rcv_saddr == inet2->rcv_saddr)); |
232 | } | 234 | } |
233 | 235 | ||
234 | int udp_v4_get_port(struct sock *sk, unsigned short snum) | 236 | int udp_v4_get_port(struct sock *sk, unsigned short snum) |
@@ -383,8 +385,8 @@ found: | |||
383 | void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable) | 385 | void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable) |
384 | { | 386 | { |
385 | struct inet_sock *inet; | 387 | struct inet_sock *inet; |
386 | struct iphdr *iph = (struct iphdr*)skb->data; | 388 | struct iphdr *iph = (struct iphdr *)skb->data; |
387 | struct udphdr *uh = (struct udphdr*)(skb->data+(iph->ihl<<2)); | 389 | struct udphdr *uh = (struct udphdr *)(skb->data+(iph->ihl<<2)); |
388 | const int type = icmp_hdr(skb)->type; | 390 | const int type = icmp_hdr(skb)->type; |
389 | const int code = icmp_hdr(skb)->code; | 391 | const int code = icmp_hdr(skb)->code; |
390 | struct sock *sk; | 392 | struct sock *sk; |
@@ -439,7 +441,7 @@ void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable) | |||
439 | if (!harderr || sk->sk_state != TCP_ESTABLISHED) | 441 | if (!harderr || sk->sk_state != TCP_ESTABLISHED) |
440 | goto out; | 442 | goto out; |
441 | } else { | 443 | } else { |
442 | ip_icmp_error(sk, skb, err, uh->dest, info, (u8*)(uh+1)); | 444 | ip_icmp_error(sk, skb, err, uh->dest, info, (u8 *)(uh+1)); |
443 | } | 445 | } |
444 | sk->sk_err = err; | 446 | sk->sk_err = err; |
445 | sk->sk_error_report(sk); | 447 | sk->sk_error_report(sk); |
@@ -474,7 +476,7 @@ EXPORT_SYMBOL(udp_flush_pending_frames); | |||
474 | * (checksum field must be zeroed out) | 476 | * (checksum field must be zeroed out) |
475 | */ | 477 | */ |
476 | static void udp4_hwcsum_outgoing(struct sock *sk, struct sk_buff *skb, | 478 | static void udp4_hwcsum_outgoing(struct sock *sk, struct sk_buff *skb, |
477 | __be32 src, __be32 dst, int len ) | 479 | __be32 src, __be32 dst, int len) |
478 | { | 480 | { |
479 | unsigned int offset; | 481 | unsigned int offset; |
480 | struct udphdr *uh = udp_hdr(skb); | 482 | struct udphdr *uh = udp_hdr(skb); |
@@ -545,7 +547,7 @@ static int udp_push_pending_frames(struct sock *sk) | |||
545 | 547 | ||
546 | } else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */ | 548 | } else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */ |
547 | 549 | ||
548 | udp4_hwcsum_outgoing(sk, skb, fl->fl4_src,fl->fl4_dst, up->len); | 550 | udp4_hwcsum_outgoing(sk, skb, fl->fl4_src, fl->fl4_dst, up->len); |
549 | goto send; | 551 | goto send; |
550 | 552 | ||
551 | } else /* `normal' UDP */ | 553 | } else /* `normal' UDP */ |
@@ -553,18 +555,24 @@ static int udp_push_pending_frames(struct sock *sk) | |||
553 | 555 | ||
554 | /* add protocol-dependent pseudo-header */ | 556 | /* add protocol-dependent pseudo-header */ |
555 | uh->check = csum_tcpudp_magic(fl->fl4_src, fl->fl4_dst, up->len, | 557 | uh->check = csum_tcpudp_magic(fl->fl4_src, fl->fl4_dst, up->len, |
556 | sk->sk_protocol, csum ); | 558 | sk->sk_protocol, csum); |
557 | if (uh->check == 0) | 559 | if (uh->check == 0) |
558 | uh->check = CSUM_MANGLED_0; | 560 | uh->check = CSUM_MANGLED_0; |
559 | 561 | ||
560 | send: | 562 | send: |
561 | err = ip_push_pending_frames(sk); | 563 | err = ip_push_pending_frames(sk); |
564 | if (err) { | ||
565 | if (err == -ENOBUFS && !inet->recverr) { | ||
566 | UDP_INC_STATS_USER(sock_net(sk), | ||
567 | UDP_MIB_SNDBUFERRORS, is_udplite); | ||
568 | err = 0; | ||
569 | } | ||
570 | } else | ||
571 | UDP_INC_STATS_USER(sock_net(sk), | ||
572 | UDP_MIB_OUTDATAGRAMS, is_udplite); | ||
562 | out: | 573 | out: |
563 | up->len = 0; | 574 | up->len = 0; |
564 | up->pending = 0; | 575 | up->pending = 0; |
565 | if (!err) | ||
566 | UDP_INC_STATS_USER(sock_net(sk), | ||
567 | UDP_MIB_OUTDATAGRAMS, is_udplite); | ||
568 | return err; | 576 | return err; |
569 | } | 577 | } |
570 | 578 | ||
@@ -592,7 +600,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | |||
592 | * Check the flags. | 600 | * Check the flags. |
593 | */ | 601 | */ |
594 | 602 | ||
595 | if (msg->msg_flags&MSG_OOB) /* Mirror BSD error message compatibility */ | 603 | if (msg->msg_flags & MSG_OOB) /* Mirror BSD error message compatibility */ |
596 | return -EOPNOTSUPP; | 604 | return -EOPNOTSUPP; |
597 | 605 | ||
598 | ipc.opt = NULL; | 606 | ipc.opt = NULL; |
@@ -619,7 +627,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | |||
619 | * Get and verify the address. | 627 | * Get and verify the address. |
620 | */ | 628 | */ |
621 | if (msg->msg_name) { | 629 | if (msg->msg_name) { |
622 | struct sockaddr_in * usin = (struct sockaddr_in*)msg->msg_name; | 630 | struct sockaddr_in * usin = (struct sockaddr_in *)msg->msg_name; |
623 | if (msg->msg_namelen < sizeof(*usin)) | 631 | if (msg->msg_namelen < sizeof(*usin)) |
624 | return -EINVAL; | 632 | return -EINVAL; |
625 | if (usin->sin_family != AF_INET) { | 633 | if (usin->sin_family != AF_INET) { |
@@ -684,7 +692,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | |||
684 | } | 692 | } |
685 | 693 | ||
686 | if (connected) | 694 | if (connected) |
687 | rt = (struct rtable*)sk_dst_check(sk, 0); | 695 | rt = (struct rtable *)sk_dst_check(sk, 0); |
688 | 696 | ||
689 | if (rt == NULL) { | 697 | if (rt == NULL) { |
690 | struct flowi fl = { .oif = ipc.oif, | 698 | struct flowi fl = { .oif = ipc.oif, |
@@ -782,6 +790,7 @@ do_confirm: | |||
782 | err = 0; | 790 | err = 0; |
783 | goto out; | 791 | goto out; |
784 | } | 792 | } |
793 | EXPORT_SYMBOL(udp_sendmsg); | ||
785 | 794 | ||
786 | int udp_sendpage(struct sock *sk, struct page *page, int offset, | 795 | int udp_sendpage(struct sock *sk, struct page *page, int offset, |
787 | size_t size, int flags) | 796 | size_t size, int flags) |
@@ -871,6 +880,7 @@ int udp_ioctl(struct sock *sk, int cmd, unsigned long arg) | |||
871 | 880 | ||
872 | return 0; | 881 | return 0; |
873 | } | 882 | } |
883 | EXPORT_SYMBOL(udp_ioctl); | ||
874 | 884 | ||
875 | /* | 885 | /* |
876 | * This should be easy, if there is something there we | 886 | * This should be easy, if there is something there we |
@@ -892,7 +902,7 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | |||
892 | * Check any passed addresses | 902 | * Check any passed addresses |
893 | */ | 903 | */ |
894 | if (addr_len) | 904 | if (addr_len) |
895 | *addr_len=sizeof(*sin); | 905 | *addr_len = sizeof(*sin); |
896 | 906 | ||
897 | if (flags & MSG_ERRQUEUE) | 907 | if (flags & MSG_ERRQUEUE) |
898 | return ip_recv_error(sk, msg, len); | 908 | return ip_recv_error(sk, msg, len); |
@@ -923,9 +933,11 @@ try_again: | |||
923 | 933 | ||
924 | if (skb_csum_unnecessary(skb)) | 934 | if (skb_csum_unnecessary(skb)) |
925 | err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), | 935 | err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), |
926 | msg->msg_iov, copied ); | 936 | msg->msg_iov, copied); |
927 | else { | 937 | else { |
928 | err = skb_copy_and_csum_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov); | 938 | err = skb_copy_and_csum_datagram_iovec(skb, |
939 | sizeof(struct udphdr), | ||
940 | msg->msg_iov); | ||
929 | 941 | ||
930 | if (err == -EINVAL) | 942 | if (err == -EINVAL) |
931 | goto csum_copy_err; | 943 | goto csum_copy_err; |
@@ -941,8 +953,7 @@ try_again: | |||
941 | sock_recv_timestamp(msg, sk, skb); | 953 | sock_recv_timestamp(msg, sk, skb); |
942 | 954 | ||
943 | /* Copy the address. */ | 955 | /* Copy the address. */ |
944 | if (sin) | 956 | if (sin) { |
945 | { | ||
946 | sin->sin_family = AF_INET; | 957 | sin->sin_family = AF_INET; |
947 | sin->sin_port = udp_hdr(skb)->source; | 958 | sin->sin_port = udp_hdr(skb)->source; |
948 | sin->sin_addr.s_addr = ip_hdr(skb)->saddr; | 959 | sin->sin_addr.s_addr = ip_hdr(skb)->saddr; |
@@ -995,6 +1006,7 @@ int udp_disconnect(struct sock *sk, int flags) | |||
995 | sk_dst_reset(sk); | 1006 | sk_dst_reset(sk); |
996 | return 0; | 1007 | return 0; |
997 | } | 1008 | } |
1009 | EXPORT_SYMBOL(udp_disconnect); | ||
998 | 1010 | ||
999 | void udp_lib_unhash(struct sock *sk) | 1011 | void udp_lib_unhash(struct sock *sk) |
1000 | { | 1012 | { |
@@ -1044,7 +1056,7 @@ drop: | |||
1044 | * Note that in the success and error cases, the skb is assumed to | 1056 | * Note that in the success and error cases, the skb is assumed to |
1045 | * have either been requeued or freed. | 1057 | * have either been requeued or freed. |
1046 | */ | 1058 | */ |
1047 | int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb) | 1059 | int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) |
1048 | { | 1060 | { |
1049 | struct udp_sock *up = udp_sk(sk); | 1061 | struct udp_sock *up = udp_sk(sk); |
1050 | int rc; | 1062 | int rc; |
@@ -1214,7 +1226,7 @@ static inline int udp4_csum_init(struct sk_buff *skb, struct udphdr *uh, | |||
1214 | if (uh->check == 0) { | 1226 | if (uh->check == 0) { |
1215 | skb->ip_summed = CHECKSUM_UNNECESSARY; | 1227 | skb->ip_summed = CHECKSUM_UNNECESSARY; |
1216 | } else if (skb->ip_summed == CHECKSUM_COMPLETE) { | 1228 | } else if (skb->ip_summed == CHECKSUM_COMPLETE) { |
1217 | if (!csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len, | 1229 | if (!csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len, |
1218 | proto, skb->csum)) | 1230 | proto, skb->csum)) |
1219 | skb->ip_summed = CHECKSUM_UNNECESSARY; | 1231 | skb->ip_summed = CHECKSUM_UNNECESSARY; |
1220 | } | 1232 | } |
@@ -1355,7 +1367,7 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, | |||
1355 | int err = 0; | 1367 | int err = 0; |
1356 | int is_udplite = IS_UDPLITE(sk); | 1368 | int is_udplite = IS_UDPLITE(sk); |
1357 | 1369 | ||
1358 | if (optlen<sizeof(int)) | 1370 | if (optlen < sizeof(int)) |
1359 | return -EINVAL; | 1371 | return -EINVAL; |
1360 | 1372 | ||
1361 | if (get_user(val, (int __user *)optval)) | 1373 | if (get_user(val, (int __user *)optval)) |
@@ -1426,6 +1438,7 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, | |||
1426 | 1438 | ||
1427 | return err; | 1439 | return err; |
1428 | } | 1440 | } |
1441 | EXPORT_SYMBOL(udp_lib_setsockopt); | ||
1429 | 1442 | ||
1430 | int udp_setsockopt(struct sock *sk, int level, int optname, | 1443 | int udp_setsockopt(struct sock *sk, int level, int optname, |
1431 | char __user *optval, int optlen) | 1444 | char __user *optval, int optlen) |
@@ -1453,7 +1466,7 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname, | |||
1453 | struct udp_sock *up = udp_sk(sk); | 1466 | struct udp_sock *up = udp_sk(sk); |
1454 | int val, len; | 1467 | int val, len; |
1455 | 1468 | ||
1456 | if (get_user(len,optlen)) | 1469 | if (get_user(len, optlen)) |
1457 | return -EFAULT; | 1470 | return -EFAULT; |
1458 | 1471 | ||
1459 | len = min_t(unsigned int, len, sizeof(int)); | 1472 | len = min_t(unsigned int, len, sizeof(int)); |
@@ -1486,10 +1499,11 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname, | |||
1486 | 1499 | ||
1487 | if (put_user(len, optlen)) | 1500 | if (put_user(len, optlen)) |
1488 | return -EFAULT; | 1501 | return -EFAULT; |
1489 | if (copy_to_user(optval, &val,len)) | 1502 | if (copy_to_user(optval, &val, len)) |
1490 | return -EFAULT; | 1503 | return -EFAULT; |
1491 | return 0; | 1504 | return 0; |
1492 | } | 1505 | } |
1506 | EXPORT_SYMBOL(udp_lib_getsockopt); | ||
1493 | 1507 | ||
1494 | int udp_getsockopt(struct sock *sk, int level, int optname, | 1508 | int udp_getsockopt(struct sock *sk, int level, int optname, |
1495 | char __user *optval, int __user *optlen) | 1509 | char __user *optval, int __user *optlen) |
@@ -1528,9 +1542,9 @@ unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait) | |||
1528 | int is_lite = IS_UDPLITE(sk); | 1542 | int is_lite = IS_UDPLITE(sk); |
1529 | 1543 | ||
1530 | /* Check for false positives due to checksum errors */ | 1544 | /* Check for false positives due to checksum errors */ |
1531 | if ( (mask & POLLRDNORM) && | 1545 | if ((mask & POLLRDNORM) && |
1532 | !(file->f_flags & O_NONBLOCK) && | 1546 | !(file->f_flags & O_NONBLOCK) && |
1533 | !(sk->sk_shutdown & RCV_SHUTDOWN)){ | 1547 | !(sk->sk_shutdown & RCV_SHUTDOWN)) { |
1534 | struct sk_buff_head *rcvq = &sk->sk_receive_queue; | 1548 | struct sk_buff_head *rcvq = &sk->sk_receive_queue; |
1535 | struct sk_buff *skb; | 1549 | struct sk_buff *skb; |
1536 | 1550 | ||
@@ -1552,6 +1566,7 @@ unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait) | |||
1552 | return mask; | 1566 | return mask; |
1553 | 1567 | ||
1554 | } | 1568 | } |
1569 | EXPORT_SYMBOL(udp_poll); | ||
1555 | 1570 | ||
1556 | struct proto udp_prot = { | 1571 | struct proto udp_prot = { |
1557 | .name = "UDP", | 1572 | .name = "UDP", |
@@ -1582,6 +1597,7 @@ struct proto udp_prot = { | |||
1582 | .compat_getsockopt = compat_udp_getsockopt, | 1597 | .compat_getsockopt = compat_udp_getsockopt, |
1583 | #endif | 1598 | #endif |
1584 | }; | 1599 | }; |
1600 | EXPORT_SYMBOL(udp_prot); | ||
1585 | 1601 | ||
1586 | /* ------------------------------------------------------------------------ */ | 1602 | /* ------------------------------------------------------------------------ */ |
1587 | #ifdef CONFIG_PROC_FS | 1603 | #ifdef CONFIG_PROC_FS |
@@ -1703,11 +1719,13 @@ int udp_proc_register(struct net *net, struct udp_seq_afinfo *afinfo) | |||
1703 | rc = -ENOMEM; | 1719 | rc = -ENOMEM; |
1704 | return rc; | 1720 | return rc; |
1705 | } | 1721 | } |
1722 | EXPORT_SYMBOL(udp_proc_register); | ||
1706 | 1723 | ||
1707 | void udp_proc_unregister(struct net *net, struct udp_seq_afinfo *afinfo) | 1724 | void udp_proc_unregister(struct net *net, struct udp_seq_afinfo *afinfo) |
1708 | { | 1725 | { |
1709 | proc_net_remove(net, afinfo->name); | 1726 | proc_net_remove(net, afinfo->name); |
1710 | } | 1727 | } |
1728 | EXPORT_SYMBOL(udp_proc_unregister); | ||
1711 | 1729 | ||
1712 | /* ------------------------------------------------------------------------ */ | 1730 | /* ------------------------------------------------------------------------ */ |
1713 | static void udp4_format_sock(struct sock *sp, struct seq_file *f, | 1731 | static void udp4_format_sock(struct sock *sp, struct seq_file *f, |
@@ -1741,7 +1759,7 @@ int udp4_seq_show(struct seq_file *seq, void *v) | |||
1741 | int len; | 1759 | int len; |
1742 | 1760 | ||
1743 | udp4_format_sock(v, seq, state->bucket, &len); | 1761 | udp4_format_sock(v, seq, state->bucket, &len); |
1744 | seq_printf(seq, "%*s\n", 127 - len ,""); | 1762 | seq_printf(seq, "%*s\n", 127 - len, ""); |
1745 | } | 1763 | } |
1746 | return 0; | 1764 | return 0; |
1747 | } | 1765 | } |
@@ -1816,16 +1834,64 @@ void __init udp_init(void) | |||
1816 | sysctl_udp_wmem_min = SK_MEM_QUANTUM; | 1834 | sysctl_udp_wmem_min = SK_MEM_QUANTUM; |
1817 | } | 1835 | } |
1818 | 1836 | ||
1819 | EXPORT_SYMBOL(udp_disconnect); | 1837 | int udp4_ufo_send_check(struct sk_buff *skb) |
1820 | EXPORT_SYMBOL(udp_ioctl); | 1838 | { |
1821 | EXPORT_SYMBOL(udp_prot); | 1839 | const struct iphdr *iph; |
1822 | EXPORT_SYMBOL(udp_sendmsg); | 1840 | struct udphdr *uh; |
1823 | EXPORT_SYMBOL(udp_lib_getsockopt); | 1841 | |
1824 | EXPORT_SYMBOL(udp_lib_setsockopt); | 1842 | if (!pskb_may_pull(skb, sizeof(*uh))) |
1825 | EXPORT_SYMBOL(udp_poll); | 1843 | return -EINVAL; |
1826 | EXPORT_SYMBOL(udp_lib_get_port); | 1844 | |
1845 | iph = ip_hdr(skb); | ||
1846 | uh = udp_hdr(skb); | ||
1847 | |||
1848 | uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len, | ||
1849 | IPPROTO_UDP, 0); | ||
1850 | skb->csum_start = skb_transport_header(skb) - skb->head; | ||
1851 | skb->csum_offset = offsetof(struct udphdr, check); | ||
1852 | skb->ip_summed = CHECKSUM_PARTIAL; | ||
1853 | return 0; | ||
1854 | } | ||
1855 | |||
1856 | struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, int features) | ||
1857 | { | ||
1858 | struct sk_buff *segs = ERR_PTR(-EINVAL); | ||
1859 | unsigned int mss; | ||
1860 | int offset; | ||
1861 | __wsum csum; | ||
1862 | |||
1863 | mss = skb_shinfo(skb)->gso_size; | ||
1864 | if (unlikely(skb->len <= mss)) | ||
1865 | goto out; | ||
1866 | |||
1867 | if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) { | ||
1868 | /* Packet is from an untrusted source, reset gso_segs. */ | ||
1869 | int type = skb_shinfo(skb)->gso_type; | ||
1870 | |||
1871 | if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY) || | ||
1872 | !(type & (SKB_GSO_UDP)))) | ||
1873 | goto out; | ||
1874 | |||
1875 | skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss); | ||
1876 | |||
1877 | segs = NULL; | ||
1878 | goto out; | ||
1879 | } | ||
1880 | |||
1881 | /* Do software UFO. Complete and fill in the UDP checksum as HW cannot | ||
1882 | * do checksum of UDP packets sent as multiple IP fragments. | ||
1883 | */ | ||
1884 | offset = skb->csum_start - skb_headroom(skb); | ||
1885 | csum = skb_checksum(skb, offset, skb->len - offset, 0); | ||
1886 | offset += skb->csum_offset; | ||
1887 | *(__sum16 *)(skb->data + offset) = csum_fold(csum); | ||
1888 | skb->ip_summed = CHECKSUM_NONE; | ||
1889 | |||
1890 | /* Fragment the skb. IP headers of the fragments are updated in | ||
1891 | * inet_gso_segment() | ||
1892 | */ | ||
1893 | segs = skb_segment(skb, features); | ||
1894 | out: | ||
1895 | return segs; | ||
1896 | } | ||
1827 | 1897 | ||
1828 | #ifdef CONFIG_PROC_FS | ||
1829 | EXPORT_SYMBOL(udp_proc_register); | ||
1830 | EXPORT_SYMBOL(udp_proc_unregister); | ||
1831 | #endif | ||