diff mbox

ip: add udp_csum, udp6_csum_tx, udp6_csum_rx control flags to ip l2tp add tunnel

Message ID 797477FE-9982-44B1-9C45-A2459FDF44C9@gmail.com
State Changes Requested, archived
Delegated to: stephen hemminger
Headers show

Commit Message

Wang Shanker April 26, 2016, 2:15 p.m. UTC
Hi, all

It’s my first time to contribute to such an important open source project. Things began when I upgraded my server, called "Server A", form ubuntu 14.04 to 16.04, which is shipped with new kernel version, 4.4. After upgrade, I soon found a l2tp tunnel between this server and another linux server, called "Server B", via ipv6 broke down. Here is the network topology:

  +----------+                    +----------+
  | Server A | -- IPV6 Network -- | Server B |
  +----------+                    +----------+
  
The l2tp tunnel was encapsulated in udp datagrams. All the configuration was normal and could work after I reverted the kernel on Server A to original version.

Here is what i did to create the tunnel:

```
on Server A:

ip l2tp add tunnel tunnel_id 86 peer_tunnel_id 86 remote 2001:db8::aaaa local 2001:db8::bbbb udp_sport 1086 udp_dport 1086
ip l2tp add session name l2tpeth0 tunnel_id 86 session_id 86 peer_session_id 86
ip l s l2tpeth0 up

on Server B:

ip l2tp add tunnel tunnel_id 86 peer_tunnel_id 86 local 2001:db8::aaaa remote 2001:db8::bbbb udp_sport 1086 udp_dport 1086
ip l2tp add session name l2tpeth0 tunnel_id 86 session_id 86 peer_session_id 86
ip l s l2tpeth0 up

```

When I used tcpdump to diagnose the problem, I got such result:

```
on Server A:

arping -i l2tpeth0 -0 1.2.3.4

on Server B:

tcpdump -i eth0 -n port 1086  -v

21:35:57.818810 IP6 (flowlabel 0x8f028, hlim 64, next-header UDP (17) payload length: 62) 2001:db8::aaaa.1086 > 2001:db8::bbbb.1086: [bad udp cksum 0x0000 -> 0x1140!] UDP, length 54
21:35:58.820572 IP6 (flowlabel 0x8f028, hlim 64, next-header UDP (17) payload length: 62) 2001:db8::aaaa.1086 > 2001:db8::bbbb.1086: [bad udp cksum 0x0000 -> 0x1140!] UDP, length 54
21:35:59.822216 IP6 (flowlabel 0x8f028, hlim 64, next-header UDP (17) payload length: 62) 2001:db8::aaaa.1086 > 2001:db8::bbbb.1086: [bad udp cksum 0x0000 -> 0x1140!] UDP, length 54

```

After looking into kernel source, I found out that in this commit a new feature to set udp6 checksum to zero in commit 6b649fe, which added `L2TP_ATTR_UDP_ZERO_CSUM6_TX` and `L2TP_ATTR_UDP_ZERO_CSUM6_TX`.

As a result, I added `udp_csum`, `udp6_csum_tx`, `udp6_csum_rx` control flags to `ip l2tp add tunnel` to control those attributes about checksum. 

Using this to create the tunnel instead on Server A:

```
ip l2tp add tunnel tunnel_id 86 peer_tunnel_id 86 remote 2001:db8::aaaa local 2001:db8::bbbb udp_sport 1086 udp_dport 1086 udp6_csum_tx on udp6_csum_rx on
```

I finally got:

```
on Server A:

arping -i l2tpeth0 -0 1.2.3.4

on Server B:

tcpdump -i eth0 -n port 1086  -v

22:07:03.844297 IP6 (flowlabel 0x8f028, hlim 64, next-header UDP (17) payload length: 62) 2001:db8::aaaa.1086 > 2001:db8::bbbb.1086: [udp sum ok] UDP, length 54
22:07:04.845717 IP6 (flowlabel 0x8f028, hlim 64, next-header UDP (17) payload length: 62) 2001:db8::aaaa.1086 > 2001:db8::bbbb.1086: [udp sum ok] UDP, length 54
22:07:05.847965 IP6 (flowlabel 0x8f028, hlim 64, next-header UDP (17) payload length: 62) 2001:db8::aaaa.1086 > 2001:db8::bbbb.1086: [udp sum ok] UDP, length 54

tcpdump -i l2tpeth0 -v

22:10:35.691326 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 1.2.3.4 tell 0.0.0.0, length 28
22:10:36.693627 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 1.2.3.4 tell 0.0.0.0, length 28
22:10:37.695010 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 1.2.3.4 tell 0.0.0.0, length 28
22:10:38.697121 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 1.2.3.4 tell 0.0.0.0, length 28

```

It seems to work. However, is it the real point that should be fixed and does my patch fix it well? I need your suggestion.


---
 ip/ipl2tp.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)
diff mbox

Patch

diff --git a/ip/ipl2tp.c b/ip/ipl2tp.c
index 3c8ee93..67a6482 100644
--- a/ip/ipl2tp.c
+++ b/ip/ipl2tp.c
@@ -56,6 +56,8 @@  struct l2tp_parm {
 
 	uint16_t pw_type;
 	uint16_t mtu;
+	int udp6_csum_tx:1;
+	int udp6_csum_rx:1;
 	int udp_csum:1;
 	int recv_seq:1;
 	int send_seq:1;
@@ -117,6 +119,9 @@  static int create_tunnel(struct l2tp_parm *p)
 	if (p->encap == L2TP_ENCAPTYPE_UDP) {
 		addattr16(&req.n, 1024, L2TP_ATTR_UDP_SPORT, p->local_udp_port);
 		addattr16(&req.n, 1024, L2TP_ATTR_UDP_DPORT, p->peer_udp_port);
+		addattr8 (&req.n, 1024, L2TP_ATTR_UDP_CSUM, p->udp_csum);
+		addattr8 (&req.n, 1024, L2TP_ATTR_UDP_ZERO_CSUM6_TX, p->udp6_csum_tx);
+		addattr8 (&req.n, 1024, L2TP_ATTR_UDP_ZERO_CSUM6_RX, p->udp6_csum_rx);
 	}
 
 	if (rtnl_talk(&genl_rth, &req.n, NULL, 0) < 0)
@@ -282,6 +287,14 @@  static int get_response(struct nlmsghdr *n, void *arg)
 		p->l2spec_len = rta_getattr_u8(attrs[L2TP_ATTR_L2SPEC_LEN]);
 
 	p->udp_csum = !!attrs[L2TP_ATTR_UDP_CSUM];
+	if (attrs[L2TP_ATTR_UDP_ZERO_CSUM6_TX])
+	  p->udp6_csum_tx = rta_getattr_u8(attrs[L2TP_ATTR_UDP_ZERO_CSUM6_TX]);
+	else
+	  p->udp6_csum_tx = 1;
+	if (attrs[L2TP_ATTR_UDP_ZERO_CSUM6_RX])
+	  p->udp6_csum_rx = rta_getattr_u8(attrs[L2TP_ATTR_UDP_ZERO_CSUM6_RX]);
+	else
+	  p->udp6_csum_rx = 1;
 	if (attrs[L2TP_ATTR_COOKIE])
 		memcpy(p->cookie, RTA_DATA(attrs[L2TP_ATTR_COOKIE]),
 		       p->cookie_len = RTA_PAYLOAD(attrs[L2TP_ATTR_COOKIE]));
@@ -470,6 +483,9 @@  static void usage(void)
 	fprintf(stderr, "          tunnel_id ID peer_tunnel_id ID\n");
 	fprintf(stderr, "          [ encap { ip | udp } ]\n");
 	fprintf(stderr, "          [ udp_sport PORT ] [ udp_dport PORT ]\n");
+	fprintf(stderr, "          [ udp_csum { on | off } ]\n");
+	fprintf(stderr, "          [ udp6_csum_tx { on | off } ]\n");
+	fprintf(stderr, "          [ udp6_csum_rx { on | off } ]\n");
 	fprintf(stderr, "Usage: ip l2tp add session [ name NAME ]\n");
 	fprintf(stderr, "          tunnel_id ID\n");
 	fprintf(stderr, "          session_id ID peer_session_id ID\n");
@@ -500,6 +516,8 @@  static int parse_args(int argc, char **argv, int cmd, struct l2tp_parm *p)
 	/* Defaults */
 	p->l2spec_type = L2TP_L2SPECTYPE_DEFAULT;
 	p->l2spec_len = 4;
+	p->udp6_csum_rx = 1;
+	p->udp6_csum_tx = 1;
 
 	while (argc > 0) {
 		if (strcmp(*argv, "encap") == 0) {
@@ -569,6 +587,33 @@  static int parse_args(int argc, char **argv, int cmd, struct l2tp_parm *p)
 			if (get_u16(&uval, *argv, 0))
 				invarg("invalid port\n", *argv);
 			p->peer_udp_port = uval;
+		} else if (strcmp(*argv, "udp_csum") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "on") == 0) {
+				p->udp_csum = 1;
+			} else if (strcmp(*argv, "off") == 0) {
+				p->udp_csum = 0;
+			} else {
+				invarg("invalid option for udp_csum\n", *argv);
+			}
+		} else if (strcmp(*argv, "udp6_csum_rx") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "on") == 0) {
+				p->udp6_csum_rx = 1;
+			} else if (strcmp(*argv, "off") == 0) {
+				p->udp6_csum_rx = 0;
+			} else {
+				invarg("invalid option for udp6_csum_rx\n", *argv);
+			}
+		} else if (strcmp(*argv, "udp6_csum_tx") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "on") == 0) {
+				p->udp6_csum_tx = 1;
+			} else if (strcmp(*argv, "off") == 0) {
+				p->udp6_csum_tx = 0;
+			} else {
+				invarg("invalid option for udp6_csum_tx\n", *argv);
+			}
 		} else if (strcmp(*argv, "offset") == 0) {
 			__u8 uval;