Commit ffcebb163c6ddba11abd2e8aabc7a8a88982e4f4

Authored by James Chapman
Committed by David S. Miller
1 parent 09a2c3c0d3

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>

Showing 1 changed file with 82 additions and 12 deletions Side-by-side Diff

drivers/net/pppol2tp.c
... ... @@ -489,6 +489,30 @@
489 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 516 /* Internal receive frame. Do the real work of receiving an L2TP data frame
493 517 * here. The skb is not on a list when we get here.
494 518 * Returns 0 if the packet was a data packet and was successfully passed on.
... ... @@ -509,6 +533,9 @@
509 533 if (tunnel == NULL)
510 534 goto no_tunnel;
511 535  
  536 + if (tunnel->sock && pppol2tp_verify_udp_checksum(tunnel->sock, skb))
  537 + goto discard_bad_csum;
  538 +
512 539 /* UDP always verifies the packet length. */
513 540 __skb_pull(skb, sizeof(struct udphdr));
514 541  
... ... @@ -725,6 +752,14 @@
725 752  
726 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 763 error:
729 764 /* Put UDP header back */
730 765 __skb_push(skb, sizeof(struct udphdr));
... ... @@ -851,7 +886,7 @@
851 886 static const unsigned char ppph[2] = { 0xff, 0x03 };
852 887 struct sock *sk = sock->sk;
853 888 struct inet_sock *inet;
854   - __wsum csum = 0;
  889 + __wsum csum;
855 890 struct sk_buff *skb;
856 891 int error;
857 892 int hdr_len;
... ... @@ -859,6 +894,8 @@
859 894 struct pppol2tp_tunnel *tunnel;
860 895 struct udphdr *uh;
861 896 unsigned int len;
  897 + struct sock *sk_tun;
  898 + u16 udp_len;
862 899  
863 900 error = -ENOTCONN;
864 901 if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED))
... ... @@ -870,7 +907,8 @@
870 907 if (session == NULL)
871 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 912 if (tunnel == NULL)
875 913 goto error_put_sess;
876 914  
877 915  
... ... @@ -893,11 +931,12 @@
893 931 skb_reset_transport_header(skb);
894 932  
895 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 936 uh = (struct udphdr *) skb->data;
898 937 uh->source = inet->sport;
899 938 uh->dest = inet->dport;
900   - uh->len = htons(hdr_len + sizeof(ppph) + total_len);
  939 + uh->len = htons(udp_len);
901 940 uh->check = 0;
902 941 skb_put(skb, sizeof(struct udphdr));
903 942  
... ... @@ -919,8 +958,22 @@
919 958 skb_put(skb, total_len);
920 959  
921 960 /* Calculate UDP checksum if configured to do so */
922   - if (session->tunnel_sock->sk_no_check != UDP_CSUM_NOXMIT)
923   - csum = udp_csum_outgoing(sk, skb);
  961 + if (sk_tun->sk_no_check == UDP_CSUM_NOXMIT)
  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 978 /* Debug */
926 979 if (session->send_seq)
927 980  
... ... @@ -1008,13 +1061,14 @@
1008 1061 struct sock *sk = (struct sock *) chan->private;
1009 1062 struct sock *sk_tun;
1010 1063 int hdr_len;
  1064 + u16 udp_len;
1011 1065 struct pppol2tp_session *session;
1012 1066 struct pppol2tp_tunnel *tunnel;
1013 1067 int rc;
1014 1068 int headroom;
1015 1069 int data_len = skb->len;
1016 1070 struct inet_sock *inet;
1017   - __wsum csum = 0;
  1071 + __wsum csum;
1018 1072 struct udphdr *uh;
1019 1073 unsigned int len;
1020 1074 int old_headroom;
... ... @@ -1060,6 +1114,8 @@
1060 1114 /* Setup L2TP header */
1061 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 1119 /* Setup UDP header */
1064 1120 inet = inet_sk(sk_tun);
1065 1121 __skb_push(skb, sizeof(*uh));
1066 1122  
... ... @@ -1067,13 +1123,9 @@
1067 1123 uh = udp_hdr(skb);
1068 1124 uh->source = inet->sport;
1069 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 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 1129 /* Debug */
1078 1130 if (session->send_seq)
1079 1131 PRINTK(session->debug, PPPOL2TP_MSG_DATA, KERN_DEBUG,
... ... @@ -1107,6 +1159,24 @@
1107 1159 dst_release(skb->dst);
1108 1160 skb->dst = dst_clone(__sk_dst_get(sk_tun));
1109 1161 pppol2tp_skb_set_owner_w(skb, sk_tun);
  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 + }
1110 1180  
1111 1181 /* Queue the packet to IP for output */
1112 1182 len = skb->len;