]> git.proxmox.com Git - mirror_iproute2.git/blobdiff - ip/ipl2tp.c
iproute: Set ip/ip6 lwtunnel flags
[mirror_iproute2.git] / ip / ipl2tp.c
index 5cd863224fda09c724baaad2d75e8c379f390fe4..f699b258a3f09b076c69254dc52fb042bb636200 100644 (file)
@@ -42,8 +42,6 @@ struct l2tp_parm {
        uint32_t peer_tunnel_id;
        uint32_t session_id;
        uint32_t peer_session_id;
-       uint32_t offset;
-       uint32_t peer_offset;
        enum l2tp_encap_type encap;
        uint16_t local_udp_port;
        uint16_t peer_udp_port;
@@ -55,14 +53,13 @@ struct l2tp_parm {
        inet_prefix peer_ip;
 
        uint16_t pw_type;
-       uint16_t mtu;
-       int udp_csum:1;
-       int recv_seq:1;
-       int send_seq:1;
-       int lns_mode:1;
-       int data_seq:2;
-       int tunnel:1;
-       int session:1;
+       unsigned int udp6_csum_tx:1;
+       unsigned int udp6_csum_rx:1;
+       unsigned int udp_csum:1;
+       unsigned int recv_seq:1;
+       unsigned int send_seq:1;
+       unsigned int tunnel:1;
+       unsigned int session:1;
        int reorder_timeout;
        const char *ifname;
        uint8_t l2spec_type;
@@ -108,18 +105,26 @@ static int create_tunnel(struct l2tp_parm *p)
 
        if (p->local_ip.family == AF_INET6)
                local_attr = L2TP_ATTR_IP6_SADDR;
-       addattr_l(&req.n, 1024, local_attr, &p->local_ip.data, p->local_ip.bytelen);
+       addattr_l(&req.n, 1024, local_attr, &p->local_ip.data,
+                 p->local_ip.bytelen);
 
        if (p->peer_ip.family == AF_INET6)
                peer_attr = L2TP_ATTR_IP6_DADDR;
-       addattr_l(&req.n, 1024, peer_attr, &p->peer_ip.data, p->peer_ip.bytelen);
+       addattr_l(&req.n, 1024, peer_attr, &p->peer_ip.data,
+                 p->peer_ip.bytelen);
 
        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);
+               if (p->udp_csum)
+                       addattr8(&req.n, 1024, L2TP_ATTR_UDP_CSUM, 1);
+               if (!p->udp6_csum_tx)
+                       addattr(&req.n, 1024, L2TP_ATTR_UDP_ZERO_CSUM6_TX);
+               if (!p->udp6_csum_rx)
+                       addattr(&req.n, 1024, L2TP_ATTR_UDP_ZERO_CSUM6_RX);
        }
 
-       if (rtnl_talk(&genl_rth, &req.n, 0, 0, NULL) < 0)
+       if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
                return -2;
 
        return 0;
@@ -132,7 +137,7 @@ static int delete_tunnel(struct l2tp_parm *p)
 
        addattr32(&req.n, 128, L2TP_ATTR_CONN_ID, p->tunnel_id);
 
-       if (rtnl_talk(&genl_rth, &req.n, 0, 0, NULL) < 0)
+       if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
                return -2;
 
        return 0;
@@ -151,22 +156,23 @@ static int create_session(struct l2tp_parm *p)
        addattr8(&req.n, 1024, L2TP_ATTR_L2SPEC_TYPE, p->l2spec_type);
        addattr8(&req.n, 1024, L2TP_ATTR_L2SPEC_LEN, p->l2spec_len);
 
-       if (p->mtu)             addattr16(&req.n, 1024, L2TP_ATTR_MTU, p->mtu);
-       if (p->recv_seq)        addattr(&req.n, 1024, L2TP_ATTR_RECV_SEQ);
-       if (p->send_seq)        addattr(&req.n, 1024, L2TP_ATTR_SEND_SEQ);
-       if (p->lns_mode)        addattr(&req.n, 1024, L2TP_ATTR_LNS_MODE);
-       if (p->data_seq)        addattr8(&req.n, 1024, L2TP_ATTR_DATA_SEQ, p->data_seq);
-       if (p->reorder_timeout) addattr64(&req.n, 1024, L2TP_ATTR_RECV_TIMEOUT,
+       if (p->recv_seq)
+               addattr8(&req.n, 1024, L2TP_ATTR_RECV_SEQ, 1);
+       if (p->send_seq)
+               addattr8(&req.n, 1024, L2TP_ATTR_SEND_SEQ, 1);
+       if (p->reorder_timeout)
+               addattr64(&req.n, 1024, L2TP_ATTR_RECV_TIMEOUT,
                                          p->reorder_timeout);
-       if (p->offset)          addattr16(&req.n, 1024, L2TP_ATTR_OFFSET, p->offset);
-       if (p->cookie_len)      addattr_l(&req.n, 1024, L2TP_ATTR_COOKIE,
-                                         p->cookie, p->cookie_len);
-       if (p->peer_cookie_len) addattr_l(&req.n, 1024, L2TP_ATTR_PEER_COOKIE,
-                                         p->peer_cookie,  p->peer_cookie_len);
-       if (p->ifname && p->ifname[0])
+       if (p->cookie_len)
+               addattr_l(&req.n, 1024, L2TP_ATTR_COOKIE,
+                         p->cookie, p->cookie_len);
+       if (p->peer_cookie_len)
+               addattr_l(&req.n, 1024, L2TP_ATTR_PEER_COOKIE,
+                         p->peer_cookie,  p->peer_cookie_len);
+       if (p->ifname)
                addattrstrz(&req.n, 1024, L2TP_ATTR_IFNAME, p->ifname);
 
-       if (rtnl_talk(&genl_rth, &req.n, 0, 0, NULL) < 0)
+       if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
                return -2;
 
        return 0;
@@ -179,21 +185,28 @@ static int delete_session(struct l2tp_parm *p)
 
        addattr32(&req.n, 1024, L2TP_ATTR_CONN_ID, p->tunnel_id);
        addattr32(&req.n, 1024, L2TP_ATTR_SESSION_ID, p->session_id);
-       if (rtnl_talk(&genl_rth, &req.n, 0, 0, NULL) < 0)
+       if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
                return -2;
 
        return 0;
 }
 
-static void print_cookie(char *name, const uint8_t *cookie, int len)
+static void print_cookie(const char *name, const char *fmt,
+                        const uint8_t *cookie, int len)
 {
-       printf("  %s %02x%02x%02x%02x", name,
-              cookie[0], cookie[1],
-              cookie[2], cookie[3]);
+       char abuf[32];
+       size_t n;
+
+       n = snprintf(abuf, sizeof(abuf),
+                    "%02x%02x%02x%02x",
+                    cookie[0], cookie[1], cookie[2], cookie[3]);
        if (len == 8)
-               printf("%02x%02x%02x%02x",
-                      cookie[4], cookie[5],
-                      cookie[6], cookie[7]);
+               snprintf(abuf + n, sizeof(abuf) - n,
+                        "%02x%02x%02x%02x",
+                        cookie[4], cookie[5],
+                        cookie[6], cookie[7]);
+
+       print_string(PRINT_ANY, name, fmt, abuf);
 }
 
 static void print_tunnel(const struct l2tp_data *data)
@@ -201,42 +214,117 @@ static void print_tunnel(const struct l2tp_data *data)
        const struct l2tp_parm *p = &data->config;
        char buf[INET6_ADDRSTRLEN];
 
-       printf("Tunnel %u, encap %s\n",
-              p->tunnel_id,
-              p->encap == L2TP_ENCAPTYPE_UDP ? "UDP" :
-              p->encap == L2TP_ENCAPTYPE_IP ? "IP" : "??");
-       printf("  From %s ", inet_ntop(p->local_ip.family, p->local_ip.data, buf, sizeof(buf)));
-       printf("to %s\n", inet_ntop(p->peer_ip.family, p->peer_ip.data, buf, sizeof(buf)));
-       printf("  Peer tunnel %u\n",
-              p->peer_tunnel_id);
-
-       if (p->encap == L2TP_ENCAPTYPE_UDP)
-               printf("  UDP source / dest ports: %hu/%hu\n",
-                      p->local_udp_port, p->peer_udp_port);
+       open_json_object(NULL);
+       print_uint(PRINT_ANY, "tunnel_id", "Tunnel %u,", p->tunnel_id);
+       print_string(PRINT_ANY, "encap", " encap %s",
+                    p->encap == L2TP_ENCAPTYPE_UDP ? "UDP" :
+                    p->encap == L2TP_ENCAPTYPE_IP ? "IP" : "??");
+       print_nl();
+
+       print_string(PRINT_ANY, "local", "  From %s ",
+                    inet_ntop(p->local_ip.family, p->local_ip.data,
+                              buf, sizeof(buf)));
+       print_string(PRINT_ANY, "peer", "to %s",
+                    inet_ntop(p->peer_ip.family, p->peer_ip.data,
+                              buf, sizeof(buf)));
+       print_nl();
+
+       print_uint(PRINT_ANY, "peer_tunnel", "  Peer tunnel %u",
+                  p->peer_tunnel_id);
+       print_nl();
+
+       if (p->encap == L2TP_ENCAPTYPE_UDP) {
+               print_string(PRINT_FP, NULL,
+                            "  UDP source / dest ports:", NULL);
+
+               print_uint(PRINT_ANY, "local_port", " %hu",
+                          p->local_udp_port);
+               print_uint(PRINT_ANY, "peer_port", "/%hu",
+                          p->peer_udp_port);
+               print_nl();
+
+               switch (p->local_ip.family) {
+               case AF_INET:
+                       print_bool(PRINT_JSON, "checksum",
+                                  NULL, p->udp_csum);
+                       print_string(PRINT_FP, NULL,
+                                    "  UDP checksum: %s\n",
+                                    p->udp_csum ? "enabled" : "disabled");
+                       break;
+               case AF_INET6:
+                       if (is_json_context()) {
+                               print_bool(PRINT_JSON, "checksum_tx",
+                                          NULL, p->udp6_csum_tx);
+
+                               print_bool(PRINT_JSON, "checksum_rx",
+                                          NULL, p->udp6_csum_tx);
+                       } else {
+                               printf("  UDP checksum: %s%s%s%s\n",
+                                      p->udp6_csum_tx && p->udp6_csum_rx
+                                      ? "enabled" : "",
+                                      p->udp6_csum_tx && !p->udp6_csum_rx
+                                      ? "tx" : "",
+                                      !p->udp6_csum_tx && p->udp6_csum_rx
+                                      ? "rx" : "",
+                                      !p->udp6_csum_tx && !p->udp6_csum_rx
+                                      ? "disabled" : "");
+                       }
+                       break;
+               }
+       }
+       close_json_object();
 }
 
 static void print_session(struct l2tp_data *data)
 {
        struct l2tp_parm *p = &data->config;
 
-       printf("Session %u in tunnel %u\n",
-              p->session_id, p->tunnel_id);
-       printf("  Peer session %u, tunnel %u\n",
-              p->peer_session_id, p->peer_tunnel_id);
+       open_json_object(NULL);
+
+       print_uint(PRINT_ANY, "session_id", "Session %u", p->session_id);
+       print_uint(PRINT_ANY, "tunnel_id",  " in tunnel %u", p->tunnel_id);
+       print_nl();
+
+       print_uint(PRINT_ANY, "peer_session_id",
+                    "  Peer session %u,", p->peer_session_id);
+       print_uint(PRINT_ANY, "peer_tunnel_id",
+                    " tunnel %u",  p->peer_tunnel_id);
+       print_nl();
 
        if (p->ifname != NULL) {
-               printf("  interface name: %s\n", p->ifname);
+               print_color_string(PRINT_ANY, COLOR_IFNAME,
+                                  "interface", "  interface name: %s" , p->ifname);
+               print_nl();
        }
-       printf("  offset %u, peer offset %u\n",
-              p->offset, p->peer_offset);
+
+       /* Show offsets only for plain console output (for legacy scripts) */
+       print_uint(PRINT_FP, "offset", "  offset %u,", 0);
+       print_uint(PRINT_FP, "peer_offset", " peer offset %u\n", 0);
+
        if (p->cookie_len > 0)
-               print_cookie("cookie", p->cookie, p->cookie_len);
+               print_cookie("cookie", "  cookie %s",
+                            p->cookie, p->cookie_len);
+
        if (p->peer_cookie_len > 0)
-               print_cookie("peer cookie", p->peer_cookie, p->peer_cookie_len);
+               print_cookie("peer_cookie", "  peer cookie %s",
+                            p->peer_cookie, p->peer_cookie_len);
+
+       if (p->reorder_timeout != 0)
+               print_uint(PRINT_ANY, "reorder_timeout",
+                          "  reorder timeout: %u", p->reorder_timeout);
+
+
+       if (p->send_seq || p->recv_seq) {
+               print_string(PRINT_FP, NULL, "%s  sequence numbering:", _SL_);
+
+               if (p->send_seq)
+                       print_null(PRINT_ANY, "send_seq", " send", NULL);
+               if (p->recv_seq)
+                       print_null(PRINT_ANY, "recv_seq", " recv", NULL);
 
-       if (p->reorder_timeout != 0) {
-               printf("  reorder timeout: %u\n", p->reorder_timeout);
        }
+       print_string(PRINT_FP, NULL, "\n", NULL);
+       close_json_object();
 }
 
 static int get_response(struct nlmsghdr *n, void *arg)
@@ -245,7 +333,7 @@ static int get_response(struct nlmsghdr *n, void *arg)
        struct l2tp_data *data = arg;
        struct l2tp_parm *p = &data->config;
        struct rtattr *attrs[L2TP_ATTR_MAX + 1];
-       struct rtattr *nla_stats;
+       struct rtattr *nla_stats, *rta;
        int len;
 
        /* Validate message and parse attributes */
@@ -263,10 +351,6 @@ static int get_response(struct nlmsghdr *n, void *arg)
                p->pw_type = rta_getattr_u16(attrs[L2TP_ATTR_PW_TYPE]);
        if (attrs[L2TP_ATTR_ENCAP_TYPE])
                p->encap = rta_getattr_u16(attrs[L2TP_ATTR_ENCAP_TYPE]);
-       if (attrs[L2TP_ATTR_OFFSET])
-               p->offset = rta_getattr_u16(attrs[L2TP_ATTR_OFFSET]);
-       if (attrs[L2TP_ATTR_DATA_SEQ])
-               p->data_seq = rta_getattr_u16(attrs[L2TP_ATTR_DATA_SEQ]);
        if (attrs[L2TP_ATTR_CONN_ID])
                p->tunnel_id = rta_getattr_u32(attrs[L2TP_ATTR_CONN_ID]);
        if (attrs[L2TP_ATTR_PEER_CONN_ID])
@@ -280,7 +364,12 @@ static int get_response(struct nlmsghdr *n, void *arg)
        if (attrs[L2TP_ATTR_L2SPEC_LEN])
                p->l2spec_len = rta_getattr_u8(attrs[L2TP_ATTR_L2SPEC_LEN]);
 
-       p->udp_csum = !!attrs[L2TP_ATTR_UDP_CSUM];
+       if (attrs[L2TP_ATTR_UDP_CSUM])
+               p->udp_csum = !!rta_getattr_u8(attrs[L2TP_ATTR_UDP_CSUM]);
+
+       p->udp6_csum_tx = !attrs[L2TP_ATTR_UDP_ZERO_CSUM6_TX];
+       p->udp6_csum_rx = !attrs[L2TP_ATTR_UDP_ZERO_CSUM6_RX];
+
        if (attrs[L2TP_ATTR_COOKIE])
                memcpy(p->cookie, RTA_DATA(attrs[L2TP_ATTR_COOKIE]),
                       p->cookie_len = RTA_PAYLOAD(attrs[L2TP_ATTR_COOKIE]));
@@ -289,41 +378,36 @@ static int get_response(struct nlmsghdr *n, void *arg)
                memcpy(p->peer_cookie, RTA_DATA(attrs[L2TP_ATTR_PEER_COOKIE]),
                       p->peer_cookie_len = RTA_PAYLOAD(attrs[L2TP_ATTR_PEER_COOKIE]));
 
-       p->recv_seq = !!attrs[L2TP_ATTR_RECV_SEQ];
-       p->send_seq = !!attrs[L2TP_ATTR_SEND_SEQ];
+       if (attrs[L2TP_ATTR_RECV_SEQ])
+               p->recv_seq = !!rta_getattr_u8(attrs[L2TP_ATTR_RECV_SEQ]);
+       if (attrs[L2TP_ATTR_SEND_SEQ])
+               p->send_seq = !!rta_getattr_u8(attrs[L2TP_ATTR_SEND_SEQ]);
 
        if (attrs[L2TP_ATTR_RECV_TIMEOUT])
                p->reorder_timeout = rta_getattr_u64(attrs[L2TP_ATTR_RECV_TIMEOUT]);
-       if (attrs[L2TP_ATTR_IP_SADDR]) {
-               p->local_ip.family = AF_INET;
-               p->local_ip.data[0] = rta_getattr_u32(attrs[L2TP_ATTR_IP_SADDR]);
-               p->local_ip.bytelen = 4;
-               p->local_ip.bitlen = -1;
-       }
-       if (attrs[L2TP_ATTR_IP_DADDR]) {
-               p->peer_ip.family = AF_INET;
-               p->peer_ip.data[0] = rta_getattr_u32(attrs[L2TP_ATTR_IP_DADDR]);
-               p->peer_ip.bytelen = 4;
-               p->peer_ip.bitlen = -1;
-       }
-       if (attrs[L2TP_ATTR_IP6_SADDR]) {
+
+       rta = attrs[L2TP_ATTR_IP_SADDR];
+       p->local_ip.family = AF_INET;
+       if (!rta) {
+               rta = attrs[L2TP_ATTR_IP6_SADDR];
                p->local_ip.family = AF_INET6;
-               memcpy(&p->local_ip.data, RTA_DATA(attrs[L2TP_ATTR_IP6_SADDR]),
-                       p->local_ip.bytelen = 16);
-               p->local_ip.bitlen = -1;
        }
-       if (attrs[L2TP_ATTR_IP6_DADDR]) {
+       if (rta && get_addr_rta(&p->local_ip, rta, p->local_ip.family))
+               return -1;
+
+       rta = attrs[L2TP_ATTR_IP_DADDR];
+       p->peer_ip.family = AF_INET;
+       if (!rta) {
+               rta = attrs[L2TP_ATTR_IP6_DADDR];
                p->peer_ip.family = AF_INET6;
-               memcpy(&p->peer_ip.data, RTA_DATA(attrs[L2TP_ATTR_IP6_DADDR]),
-                       p->peer_ip.bytelen = 16);
-               p->peer_ip.bitlen = -1;
        }
+       if (rta && get_addr_rta(&p->peer_ip, rta, p->peer_ip.family))
+               return -1;
+
        if (attrs[L2TP_ATTR_UDP_SPORT])
                p->local_udp_port = rta_getattr_u16(attrs[L2TP_ATTR_UDP_SPORT]);
        if (attrs[L2TP_ATTR_UDP_DPORT])
                p->peer_udp_port = rta_getattr_u16(attrs[L2TP_ATTR_UDP_DPORT]);
-       if (attrs[L2TP_ATTR_MTU])
-               p->mtu = rta_getattr_u16(attrs[L2TP_ATTR_MTU]);
        if (attrs[L2TP_ATTR_IFNAME])
                p->ifname = rta_getattr_str(attrs[L2TP_ATTR_IFNAME]);
 
@@ -354,7 +438,7 @@ static int get_response(struct nlmsghdr *n, void *arg)
        return 0;
 }
 
-static int session_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+static int session_nlmsg(struct nlmsghdr *n, void *arg)
 {
        int ret = get_response(n, arg);
 
@@ -374,21 +458,25 @@ static int get_session(struct l2tp_data *p)
 
        if (p->config.tunnel_id && p->config.session_id) {
                addattr32(&req.n, 128, L2TP_ATTR_CONN_ID, p->config.tunnel_id);
-               addattr32(&req.n, 128, L2TP_ATTR_SESSION_ID, p->config.session_id);
+               addattr32(&req.n, 128, L2TP_ATTR_SESSION_ID,
+                         p->config.session_id);
        }
 
        if (rtnl_send(&genl_rth, &req, req.n.nlmsg_len) < 0)
                return -2;
 
+       new_json_obj(json);
        if (rtnl_dump_filter(&genl_rth, session_nlmsg, p) < 0) {
                fprintf(stderr, "Dump terminated\n");
                exit(1);
        }
+       delete_json_obj();
+       fflush(stdout);
 
        return 0;
 }
 
-static int tunnel_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+static int tunnel_nlmsg(struct nlmsghdr *n, void *arg)
 {
        int ret = get_response(n, arg);
 
@@ -412,10 +500,13 @@ static int get_tunnel(struct l2tp_data *p)
        if (rtnl_send(&genl_rth, &req, req.n.nlmsg_len) < 0)
                return -2;
 
+       new_json_obj(json);
        if (rtnl_dump_filter(&genl_rth, tunnel_nlmsg, p) < 0) {
                fprintf(stderr, "Dump terminated\n");
                exit(1);
        }
+       delete_json_obj();
+       fflush(stdout);
 
        return 0;
 }
@@ -424,68 +515,36 @@ static int get_tunnel(struct l2tp_data *p)
  * Command parser
  *****************************************************************************/
 
-static int hex(char ch)
-{
-       if ((ch >= 'a') && (ch <= 'f'))
-               return ch - 'a' + 10;
-       if ((ch >= '0') && (ch <= '9'))
-               return ch - '0';
-       if ((ch >= 'A') && (ch <= 'F'))
-               return ch - 'A' + 10;
-       return -1;
-}
-
-static int hex2mem(const char *buf, uint8_t *mem, int count)
-{
-       int i, j;
-       int c;
-
-       for (i = 0, j = 0; i < count; i++, j += 2) {
-               c = hex(buf[j]);
-               if (c < 0)
-                       goto err;
-
-               mem[i] = c << 4;
-
-               c = hex(buf[j + 1]);
-               if (c < 0)
-                       goto err;
-
-               mem[i] |= c;
-       }
-
-       return 0;
-
-err:
-       return -1;
-}
-
 static void usage(void) __attribute__((noreturn));
 
 static void usage(void)
 {
-       fprintf(stderr, "Usage: ip l2tp add tunnel\n");
-       fprintf(stderr, "          remote ADDR local ADDR\n");
-       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, "Usage: ip l2tp add session [ name NAME ]\n");
-       fprintf(stderr, "          tunnel_id ID\n");
-       fprintf(stderr, "          session_id ID peer_session_id ID\n");
-       fprintf(stderr, "          [ cookie HEXSTR ] [ peer_cookie HEXSTR ]\n");
-       fprintf(stderr, "          [ offset OFFSET ] [ peer_offset OFFSET ]\n");
-       fprintf(stderr, "          [ l2spec_type L2SPEC ]\n");
-       fprintf(stderr, "       ip l2tp del tunnel tunnel_id ID\n");
-       fprintf(stderr, "       ip l2tp del session tunnel_id ID session_id ID\n");
-       fprintf(stderr, "       ip l2tp show tunnel [ tunnel_id ID ]\n");
-       fprintf(stderr, "       ip l2tp show session [ tunnel_id ID ] [ session_id ID ]\n");
-       fprintf(stderr, "\n");
-       fprintf(stderr, "Where: NAME   := STRING\n");
-       fprintf(stderr, "       ADDR   := { IP_ADDRESS | any }\n");
-       fprintf(stderr, "       PORT   := { 0..65535 }\n");
-       fprintf(stderr, "       ID     := { 1..4294967295 }\n");
-       fprintf(stderr, "       HEXSTR := { 8 or 16 hex digits (4 / 8 bytes) }\n");
-       fprintf(stderr, "       L2SPEC := { none | default }\n");
+       fprintf(stderr, "Usage: ip l2tp add tunnel\n"
+               "          remote ADDR local ADDR\n"
+               "          tunnel_id ID peer_tunnel_id ID\n"
+               "          [ encap { ip | udp } ]\n"
+               "          [ udp_sport PORT ] [ udp_dport PORT ]\n"
+               "          [ udp_csum { on | off } ]\n"
+               "          [ udp6_csum_tx { on | off } ]\n"
+               "          [ udp6_csum_rx { on | off } ]\n"
+               "Usage: ip l2tp add session [ name NAME ]\n"
+               "          tunnel_id ID\n"
+               "          session_id ID peer_session_id ID\n"
+               "          [ cookie HEXSTR ] [ peer_cookie HEXSTR ]\n"
+               "          [ seq { none | send | recv | both } ]\n"
+               "          [ l2spec_type L2SPEC ]\n"
+               "       ip l2tp del tunnel tunnel_id ID\n"
+               "       ip l2tp del session tunnel_id ID session_id ID\n"
+               "       ip l2tp show tunnel [ tunnel_id ID ]\n"
+               "       ip l2tp show session [ tunnel_id ID ] [ session_id ID ]\n"
+               "\n"
+               "Where: NAME   := STRING\n"
+               "       ADDR   := { IP_ADDRESS | any }\n"
+               "       PORT   := { 0..65535 }\n"
+               "       ID     := { 1..4294967295 }\n"
+               "       HEXSTR := { 8 or 16 hex digits (4 / 8 bytes) }\n"
+               "       L2SPEC := { none | default }\n");
+
        exit(-1);
 }
 
@@ -499,6 +558,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) {
@@ -513,6 +574,8 @@ static int parse_args(int argc, char **argv, int cmd, struct l2tp_parm *p)
                        }
                } else if (strcmp(*argv, "name") == 0) {
                        NEXT_ARG();
+                       if (check_ifname(*argv))
+                               invarg("\"name\" not a valid ifname", *argv);
                        p->ifname = *argv;
                } else if (strcmp(*argv, "remote") == 0) {
                        NEXT_ARG();
@@ -525,6 +588,7 @@ static int parse_args(int argc, char **argv, int cmd, struct l2tp_parm *p)
                } else if ((strcmp(*argv, "tunnel_id") == 0) ||
                           (strcmp(*argv, "tid") == 0)) {
                        __u32 uval;
+
                        NEXT_ARG();
                        if (get_u32(&uval, *argv, 0))
                                invarg("invalid ID\n", *argv);
@@ -532,6 +596,7 @@ static int parse_args(int argc, char **argv, int cmd, struct l2tp_parm *p)
                } else if ((strcmp(*argv, "peer_tunnel_id") == 0) ||
                           (strcmp(*argv, "ptid") == 0)) {
                        __u32 uval;
+
                        NEXT_ARG();
                        if (get_u32(&uval, *argv, 0))
                                invarg("invalid ID\n", *argv);
@@ -539,6 +604,7 @@ static int parse_args(int argc, char **argv, int cmd, struct l2tp_parm *p)
                } else if ((strcmp(*argv, "session_id") == 0) ||
                           (strcmp(*argv, "sid") == 0)) {
                        __u32 uval;
+
                        NEXT_ARG();
                        if (get_u32(&uval, *argv, 0))
                                invarg("invalid ID\n", *argv);
@@ -546,36 +612,60 @@ static int parse_args(int argc, char **argv, int cmd, struct l2tp_parm *p)
                } else if ((strcmp(*argv, "peer_session_id") == 0) ||
                           (strcmp(*argv, "psid") == 0)) {
                        __u32 uval;
+
                        NEXT_ARG();
                        if (get_u32(&uval, *argv, 0))
                                invarg("invalid ID\n", *argv);
                        p->peer_session_id = uval;
                } else if (strcmp(*argv, "udp_sport") == 0) {
                        __u16 uval;
+
                        NEXT_ARG();
                        if (get_u16(&uval, *argv, 0))
                                invarg("invalid port\n", *argv);
                        p->local_udp_port = uval;
                } else if (strcmp(*argv, "udp_dport") == 0) {
                        __u16 uval;
+
                        NEXT_ARG();
                        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;
+                       fprintf(stderr, "Ignoring option \"offset\"\n");
                        NEXT_ARG();
-                       if (get_u8(&uval, *argv, 0))
-                               invarg("invalid offset\n", *argv);
-                       p->offset = uval;
                } else if (strcmp(*argv, "peer_offset") == 0) {
-                       __u8 uval;
+                       fprintf(stderr, "Ignoring option \"peer_offset\"\n");
                        NEXT_ARG();
-                       if (get_u8(&uval, *argv, 0))
-                               invarg("invalid offset\n", *argv);
-                       p->peer_offset = uval;
                } else if (strcmp(*argv, "cookie") == 0) {
                        int slen;
+
                        NEXT_ARG();
                        slen = strlen(*argv);
                        if ((slen != 8) && (slen != 16))
@@ -586,6 +676,7 @@ static int parse_args(int argc, char **argv, int cmd, struct l2tp_parm *p)
                                invarg("cookie must be a hex string\n", *argv);
                } else if (strcmp(*argv, "peer_cookie") == 0) {
                        int slen;
+
                        NEXT_ARG();
                        slen = strlen(*argv);
                        if ((slen != 8) && (slen != 16))
@@ -603,7 +694,26 @@ static int parse_args(int argc, char **argv, int cmd, struct l2tp_parm *p)
                                p->l2spec_type = L2TP_L2SPECTYPE_NONE;
                                p->l2spec_len = 0;
                        } else {
-                               fprintf(stderr, "Unknown layer2specific header type \"%s\"\n", *argv);
+                               fprintf(stderr,
+                                       "Unknown layer2specific header type \"%s\"\n",
+                                       *argv);
+                               exit(-1);
+                       }
+               } else if (strcmp(*argv, "seq") == 0) {
+                       NEXT_ARG();
+                       if (strcasecmp(*argv, "both") == 0) {
+                               p->recv_seq = 1;
+                               p->send_seq = 1;
+                       } else if (strcasecmp(*argv, "recv") == 0) {
+                               p->recv_seq = 1;
+                       } else if (strcasecmp(*argv, "send") == 0) {
+                               p->send_seq = 1;
+                       } else if (strcasecmp(*argv, "none") == 0) {
+                               p->recv_seq = 0;
+                               p->send_seq = 0;
+                       } else {
+                               fprintf(stderr,
+                                       "Unknown seq value \"%s\"\n", *argv);
                                exit(-1);
                        }
                } else if (strcmp(*argv, "tunnel") == 0) {
@@ -719,20 +829,12 @@ static int do_show(int argc, char **argv)
 
 int do_ipl2tp(int argc, char **argv)
 {
-       if (genl_family < 0) {
-               if (rtnl_open_byproto(&genl_rth, 0, NETLINK_GENERIC) < 0) {
-                       fprintf(stderr, "Cannot open generic netlink socket\n");
-                       exit(1);
-               }
-
-               genl_family = genl_resolve_family(&genl_rth, L2TP_GENL_NAME);
-               if (genl_family < 0)
-                       exit(1);
-       }
-
-       if (argc < 1)
+       if (argc < 1 || !matches(*argv, "help"))
                usage();
 
+       if (genl_init_handle(&genl_rth, L2TP_GENL_NAME, &genl_family))
+               exit(1);
+
        if (matches(*argv, "add") == 0)
                return do_add(argc-1, argv+1);
        if (matches(*argv, "delete") == 0)
@@ -741,9 +843,8 @@ int do_ipl2tp(int argc, char **argv)
            matches(*argv, "lst") == 0 ||
            matches(*argv, "list") == 0)
                return do_show(argc-1, argv+1);
-       if (matches(*argv, "help") == 0)
-               usage();
 
-       fprintf(stderr, "Command \"%s\" is unknown, try \"ip l2tp help\".\n", *argv);
+       fprintf(stderr,
+               "Command \"%s\" is unknown, try \"ip l2tp help\".\n", *argv);
        exit(-1);
 }