diff options
author | James Chapman <jchapman@katalix.com> | 2008-12-16 04:23:49 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-12-16 04:23:49 -0500 |
commit | ffcebb163c6ddba11abd2e8aabc7a8a88982e4f4 (patch) | |
tree | b531cdc5afb34dae85d67aef5607d344996e1f44 /drivers | |
parent | 09a2c3c0d3d3550675b766aa5edc28ffee79d7ab (diff) |
l2tp: fix UDP checksum support
The pppol2tp driver has had broken UDP checksum code for a long
time. This patch fixes it. If UDP checksums are enabled in the
tunnel's UDP socket, the L2TP driver now properly validates the
checksum on receive and fills in the checksum on transmit. If the
network device has hardware checksum support and is enabled, it is
used instead of generating/checking the checksum in software.
Signed-off-by: James Chapman <jchapman@katalix.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/pppol2tp.c | 94 |
1 files changed, 82 insertions, 12 deletions
diff --git a/drivers/net/pppol2tp.c b/drivers/net/pppol2tp.c index e98d9773158d..f1a946785c6a 100644 --- a/drivers/net/pppol2tp.c +++ b/drivers/net/pppol2tp.c | |||
@@ -489,6 +489,30 @@ out: | |||
489 | spin_unlock_bh(&session->reorder_q.lock); | 489 | spin_unlock_bh(&session->reorder_q.lock); |
490 | } | 490 | } |
491 | 491 | ||
492 | static inline int pppol2tp_verify_udp_checksum(struct sock *sk, | ||
493 | struct sk_buff *skb) | ||
494 | { | ||
495 | struct udphdr *uh = udp_hdr(skb); | ||
496 | u16 ulen = ntohs(uh->len); | ||
497 | struct inet_sock *inet; | ||
498 | __wsum psum; | ||
499 | |||
500 | if (sk->sk_no_check || skb_csum_unnecessary(skb) || !uh->check) | ||
501 | return 0; | ||
502 | |||
503 | inet = inet_sk(sk); | ||
504 | psum = csum_tcpudp_nofold(inet->saddr, inet->daddr, ulen, | ||
505 | IPPROTO_UDP, 0); | ||
506 | |||
507 | if ((skb->ip_summed == CHECKSUM_COMPLETE) && | ||
508 | !csum_fold(csum_add(psum, skb->csum))) | ||
509 | return 0; | ||
510 | |||
511 | skb->csum = psum; | ||
512 | |||
513 | return __skb_checksum_complete(skb); | ||
514 | } | ||
515 | |||
492 | /* Internal receive frame. Do the real work of receiving an L2TP data frame | 516 | /* Internal receive frame. Do the real work of receiving an L2TP data frame |
493 | * here. The skb is not on a list when we get here. | 517 | * here. The skb is not on a list when we get here. |
494 | * Returns 0 if the packet was a data packet and was successfully passed on. | 518 | * Returns 0 if the packet was a data packet and was successfully passed on. |
@@ -509,6 +533,9 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb) | |||
509 | if (tunnel == NULL) | 533 | if (tunnel == NULL) |
510 | goto no_tunnel; | 534 | goto no_tunnel; |
511 | 535 | ||
536 | if (tunnel->sock && pppol2tp_verify_udp_checksum(tunnel->sock, skb)) | ||
537 | goto discard_bad_csum; | ||
538 | |||
512 | /* UDP always verifies the packet length. */ | 539 | /* UDP always verifies the packet length. */ |
513 | __skb_pull(skb, sizeof(struct udphdr)); | 540 | __skb_pull(skb, sizeof(struct udphdr)); |
514 | 541 | ||
@@ -725,6 +752,14 @@ discard: | |||
725 | 752 | ||
726 | return 0; | 753 | return 0; |
727 | 754 | ||
755 | discard_bad_csum: | ||
756 | LIMIT_NETDEBUG("%s: UDP: bad checksum\n", tunnel->name); | ||
757 | UDP_INC_STATS_USER(&init_net, UDP_MIB_INERRORS, 0); | ||
758 | tunnel->stats.rx_errors++; | ||
759 | kfree_skb(skb); | ||
760 | |||
761 | return 0; | ||
762 | |||
728 | error: | 763 | error: |
729 | /* Put UDP header back */ | 764 | /* Put UDP header back */ |
730 | __skb_push(skb, sizeof(struct udphdr)); | 765 | __skb_push(skb, sizeof(struct udphdr)); |
@@ -851,7 +886,7 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh | |||
851 | static const unsigned char ppph[2] = { 0xff, 0x03 }; | 886 | static const unsigned char ppph[2] = { 0xff, 0x03 }; |
852 | struct sock *sk = sock->sk; | 887 | struct sock *sk = sock->sk; |
853 | struct inet_sock *inet; | 888 | struct inet_sock *inet; |
854 | __wsum csum = 0; | 889 | __wsum csum; |
855 | struct sk_buff *skb; | 890 | struct sk_buff *skb; |
856 | int error; | 891 | int error; |
857 | int hdr_len; | 892 | int hdr_len; |
@@ -859,6 +894,8 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh | |||
859 | struct pppol2tp_tunnel *tunnel; | 894 | struct pppol2tp_tunnel *tunnel; |
860 | struct udphdr *uh; | 895 | struct udphdr *uh; |
861 | unsigned int len; | 896 | unsigned int len; |
897 | struct sock *sk_tun; | ||
898 | u16 udp_len; | ||
862 | 899 | ||
863 | error = -ENOTCONN; | 900 | error = -ENOTCONN; |
864 | if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED)) | 901 | if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED)) |
@@ -870,7 +907,8 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh | |||
870 | if (session == NULL) | 907 | if (session == NULL) |
871 | goto error; | 908 | goto error; |
872 | 909 | ||
873 | tunnel = pppol2tp_sock_to_tunnel(session->tunnel_sock); | 910 | sk_tun = session->tunnel_sock; |
911 | tunnel = pppol2tp_sock_to_tunnel(sk_tun); | ||
874 | if (tunnel == NULL) | 912 | if (tunnel == NULL) |
875 | goto error_put_sess; | 913 | goto error_put_sess; |
876 | 914 | ||
@@ -893,11 +931,12 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh | |||
893 | skb_reset_transport_header(skb); | 931 | skb_reset_transport_header(skb); |
894 | 932 | ||
895 | /* Build UDP header */ | 933 | /* Build UDP header */ |
896 | inet = inet_sk(session->tunnel_sock); | 934 | inet = inet_sk(sk_tun); |
935 | udp_len = hdr_len + sizeof(ppph) + total_len; | ||
897 | uh = (struct udphdr *) skb->data; | 936 | uh = (struct udphdr *) skb->data; |
898 | uh->source = inet->sport; | 937 | uh->source = inet->sport; |
899 | uh->dest = inet->dport; | 938 | uh->dest = inet->dport; |
900 | uh->len = htons(hdr_len + sizeof(ppph) + total_len); | 939 | uh->len = htons(udp_len); |
901 | uh->check = 0; | 940 | uh->check = 0; |
902 | skb_put(skb, sizeof(struct udphdr)); | 941 | skb_put(skb, sizeof(struct udphdr)); |
903 | 942 | ||
@@ -919,8 +958,22 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh | |||
919 | skb_put(skb, total_len); | 958 | skb_put(skb, total_len); |
920 | 959 | ||
921 | /* Calculate UDP checksum if configured to do so */ | 960 | /* Calculate UDP checksum if configured to do so */ |
922 | if (session->tunnel_sock->sk_no_check != UDP_CSUM_NOXMIT) | 961 | if (sk_tun->sk_no_check == UDP_CSUM_NOXMIT) |
923 | csum = udp_csum_outgoing(sk, skb); | 962 | skb->ip_summed = CHECKSUM_NONE; |
963 | else if (!(skb->dst->dev->features & NETIF_F_V4_CSUM)) { | ||
964 | skb->ip_summed = CHECKSUM_COMPLETE; | ||
965 | csum = skb_checksum(skb, 0, udp_len, 0); | ||
966 | uh->check = csum_tcpudp_magic(inet->saddr, inet->daddr, | ||
967 | udp_len, IPPROTO_UDP, csum); | ||
968 | if (uh->check == 0) | ||
969 | uh->check = CSUM_MANGLED_0; | ||
970 | } else { | ||
971 | skb->ip_summed = CHECKSUM_PARTIAL; | ||
972 | skb->csum_start = skb_transport_header(skb) - skb->head; | ||
973 | skb->csum_offset = offsetof(struct udphdr, check); | ||
974 | uh->check = ~csum_tcpudp_magic(inet->saddr, inet->daddr, | ||
975 | udp_len, IPPROTO_UDP, 0); | ||
976 | } | ||
924 | 977 | ||
925 | /* Debug */ | 978 | /* Debug */ |
926 | if (session->send_seq) | 979 | if (session->send_seq) |
@@ -1008,13 +1061,14 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) | |||
1008 | struct sock *sk = (struct sock *) chan->private; | 1061 | struct sock *sk = (struct sock *) chan->private; |
1009 | struct sock *sk_tun; | 1062 | struct sock *sk_tun; |
1010 | int hdr_len; | 1063 | int hdr_len; |
1064 | u16 udp_len; | ||
1011 | struct pppol2tp_session *session; | 1065 | struct pppol2tp_session *session; |
1012 | struct pppol2tp_tunnel *tunnel; | 1066 | struct pppol2tp_tunnel *tunnel; |
1013 | int rc; | 1067 | int rc; |
1014 | int headroom; | 1068 | int headroom; |
1015 | int data_len = skb->len; | 1069 | int data_len = skb->len; |
1016 | struct inet_sock *inet; | 1070 | struct inet_sock *inet; |
1017 | __wsum csum = 0; | 1071 | __wsum csum; |
1018 | struct udphdr *uh; | 1072 | struct udphdr *uh; |
1019 | unsigned int len; | 1073 | unsigned int len; |
1020 | int old_headroom; | 1074 | int old_headroom; |
@@ -1060,6 +1114,8 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) | |||
1060 | /* Setup L2TP header */ | 1114 | /* Setup L2TP header */ |
1061 | pppol2tp_build_l2tp_header(session, __skb_push(skb, hdr_len)); | 1115 | pppol2tp_build_l2tp_header(session, __skb_push(skb, hdr_len)); |
1062 | 1116 | ||
1117 | udp_len = sizeof(struct udphdr) + hdr_len + sizeof(ppph) + data_len; | ||
1118 | |||
1063 | /* Setup UDP header */ | 1119 | /* Setup UDP header */ |
1064 | inet = inet_sk(sk_tun); | 1120 | inet = inet_sk(sk_tun); |
1065 | __skb_push(skb, sizeof(*uh)); | 1121 | __skb_push(skb, sizeof(*uh)); |
@@ -1067,13 +1123,9 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) | |||
1067 | uh = udp_hdr(skb); | 1123 | uh = udp_hdr(skb); |
1068 | uh->source = inet->sport; | 1124 | uh->source = inet->sport; |
1069 | uh->dest = inet->dport; | 1125 | uh->dest = inet->dport; |
1070 | uh->len = htons(sizeof(struct udphdr) + hdr_len + sizeof(ppph) + data_len); | 1126 | uh->len = htons(udp_len); |
1071 | uh->check = 0; | 1127 | uh->check = 0; |
1072 | 1128 | ||
1073 | /* *BROKEN* Calculate UDP checksum if configured to do so */ | ||
1074 | if (sk_tun->sk_no_check != UDP_CSUM_NOXMIT) | ||
1075 | csum = udp_csum_outgoing(sk_tun, skb); | ||
1076 | |||
1077 | /* Debug */ | 1129 | /* Debug */ |
1078 | if (session->send_seq) | 1130 | if (session->send_seq) |
1079 | PRINTK(session->debug, PPPOL2TP_MSG_DATA, KERN_DEBUG, | 1131 | PRINTK(session->debug, PPPOL2TP_MSG_DATA, KERN_DEBUG, |
@@ -1108,6 +1160,24 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) | |||
1108 | skb->dst = dst_clone(__sk_dst_get(sk_tun)); | 1160 | skb->dst = dst_clone(__sk_dst_get(sk_tun)); |
1109 | pppol2tp_skb_set_owner_w(skb, sk_tun); | 1161 | pppol2tp_skb_set_owner_w(skb, sk_tun); |
1110 | 1162 | ||
1163 | /* Calculate UDP checksum if configured to do so */ | ||
1164 | if (sk_tun->sk_no_check == UDP_CSUM_NOXMIT) | ||
1165 | skb->ip_summed = CHECKSUM_NONE; | ||
1166 | else if (!(skb->dst->dev->features & NETIF_F_V4_CSUM)) { | ||
1167 | skb->ip_summed = CHECKSUM_COMPLETE; | ||
1168 | csum = skb_checksum(skb, 0, udp_len, 0); | ||
1169 | uh->check = csum_tcpudp_magic(inet->saddr, inet->daddr, | ||
1170 | udp_len, IPPROTO_UDP, csum); | ||
1171 | if (uh->check == 0) | ||
1172 | uh->check = CSUM_MANGLED_0; | ||
1173 | } else { | ||
1174 | skb->ip_summed = CHECKSUM_PARTIAL; | ||
1175 | skb->csum_start = skb_transport_header(skb) - skb->head; | ||
1176 | skb->csum_offset = offsetof(struct udphdr, check); | ||
1177 | uh->check = ~csum_tcpudp_magic(inet->saddr, inet->daddr, | ||
1178 | udp_len, IPPROTO_UDP, 0); | ||
1179 | } | ||
1180 | |||
1111 | /* Queue the packet to IP for output */ | 1181 | /* Queue the packet to IP for output */ |
1112 | len = skb->len; | 1182 | len = skb->len; |
1113 | rc = ip_queue_xmit(skb, 1); | 1183 | rc = ip_queue_xmit(skb, 1); |