mirror_iproute2/ip/ipl2tp.c
Phil Sutter 625df645b7 Check user supplied interface name lengths
The original problem was that something like:

| strncpy(ifr.ifr_name, *argv, IFNAMSIZ);

might leave ifr.ifr_name unterminated if length of *argv exceeds
IFNAMSIZ. In order to fix this, I thought about replacing all those
cases with (equivalent) calls to snprintf() or even introducing
strlcpy(). But as Ulrich Drepper correctly pointed out when rejecting
the latter from being added to glibc, truncating a string without
notifying the user is not to be considered good practice. So let's
excercise what he suggested and reject empty, overlong or otherwise
invalid interface names right from the start - this way calls to
strncpy() like shown above become safe and the user has a chance to
reconsider what he was trying to do.

Note that this doesn't add calls to check_ifname() to all places where
user supplied interface name is parsed. In many cases, the interface
must exist already and is therefore looked up using ll_name_to_index(),
so if_nametoindex() will perform the necessary checks already.

Signed-off-by: Phil Sutter <phil@nwl.cc>
2017-10-02 08:01:21 -07:00

830 lines
22 KiB
C

/*
* ipl2tp.c "ip l2tp"
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Original Author: James Chapman <jchapman@katalix.com>
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <linux/if_arp.h>
#include <linux/ip.h>
#include <linux/genetlink.h>
#include <linux/l2tp.h>
#include "libgenl.h"
#include "utils.h"
#include "ip_common.h"
enum {
L2TP_ADD,
L2TP_CHG,
L2TP_DEL,
L2TP_GET
};
struct l2tp_parm {
uint32_t tunnel_id;
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;
int cookie_len;
uint8_t cookie[8];
int peer_cookie_len;
uint8_t peer_cookie[8];
inet_prefix local_ip;
inet_prefix peer_ip;
uint16_t pw_type;
uint16_t mtu;
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 lns_mode:1;
unsigned int data_seq:2;
unsigned int tunnel:1;
unsigned int session:1;
int reorder_timeout;
const char *ifname;
uint8_t l2spec_type;
uint8_t l2spec_len;
};
struct l2tp_stats {
uint64_t data_rx_packets;
uint64_t data_rx_bytes;
uint64_t data_rx_errors;
uint64_t data_rx_oos_packets;
uint64_t data_rx_oos_discards;
uint64_t data_tx_packets;
uint64_t data_tx_bytes;
uint64_t data_tx_errors;
};
struct l2tp_data {
struct l2tp_parm config;
struct l2tp_stats stats;
};
/* netlink socket */
static struct rtnl_handle genl_rth;
static int genl_family = -1;
/*****************************************************************************
* Netlink actions
*****************************************************************************/
static int create_tunnel(struct l2tp_parm *p)
{
uint32_t local_attr = L2TP_ATTR_IP_SADDR;
uint32_t peer_attr = L2TP_ATTR_IP_DADDR;
GENL_REQUEST(req, 1024, genl_family, 0, L2TP_GENL_VERSION,
L2TP_CMD_TUNNEL_CREATE, NLM_F_REQUEST | NLM_F_ACK);
addattr32(&req.n, 1024, L2TP_ATTR_CONN_ID, p->tunnel_id);
addattr32(&req.n, 1024, L2TP_ATTR_PEER_CONN_ID, p->peer_tunnel_id);
addattr8(&req.n, 1024, L2TP_ATTR_PROTO_VERSION, 3);
addattr16(&req.n, 1024, L2TP_ATTR_ENCAP_TYPE, p->encap);
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);
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);
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, NULL, 0) < 0)
return -2;
return 0;
}
static int delete_tunnel(struct l2tp_parm *p)
{
GENL_REQUEST(req, 128, genl_family, 0, L2TP_GENL_VERSION,
L2TP_CMD_TUNNEL_DELETE, NLM_F_REQUEST | NLM_F_ACK);
addattr32(&req.n, 128, L2TP_ATTR_CONN_ID, p->tunnel_id);
if (rtnl_talk(&genl_rth, &req.n, NULL, 0) < 0)
return -2;
return 0;
}
static int create_session(struct l2tp_parm *p)
{
GENL_REQUEST(req, 1024, genl_family, 0, L2TP_GENL_VERSION,
L2TP_CMD_SESSION_CREATE, NLM_F_REQUEST | NLM_F_ACK);
addattr32(&req.n, 1024, L2TP_ATTR_CONN_ID, p->tunnel_id);
addattr32(&req.n, 1024, L2TP_ATTR_PEER_CONN_ID, p->peer_tunnel_id);
addattr32(&req.n, 1024, L2TP_ATTR_SESSION_ID, p->session_id);
addattr32(&req.n, 1024, L2TP_ATTR_PEER_SESSION_ID, p->peer_session_id);
addattr16(&req.n, 1024, L2TP_ATTR_PW_TYPE, p->pw_type);
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)
addattr8(&req.n, 1024, L2TP_ATTR_RECV_SEQ, 1);
if (p->send_seq)
addattr8(&req.n, 1024, L2TP_ATTR_SEND_SEQ, 1);
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,
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)
addattrstrz(&req.n, 1024, L2TP_ATTR_IFNAME, p->ifname);
if (rtnl_talk(&genl_rth, &req.n, NULL, 0) < 0)
return -2;
return 0;
}
static int delete_session(struct l2tp_parm *p)
{
GENL_REQUEST(req, 1024, genl_family, 0, L2TP_GENL_VERSION,
L2TP_CMD_SESSION_DELETE, NLM_F_REQUEST | NLM_F_ACK);
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, NULL, 0) < 0)
return -2;
return 0;
}
static void print_cookie(char *name, const uint8_t *cookie, int len)
{
printf(" %s %02x%02x%02x%02x", name,
cookie[0], cookie[1],
cookie[2], cookie[3]);
if (len == 8)
printf("%02x%02x%02x%02x",
cookie[4], cookie[5],
cookie[6], cookie[7]);
}
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);
switch (p->local_ip.family) {
case AF_INET:
printf(" UDP checksum: %s\n",
p->udp_csum ? "enabled" : "disabled");
break;
case AF_INET6:
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;
}
}
}
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);
if (p->ifname != NULL)
printf(" interface name: %s\n", p->ifname);
printf(" offset %u, peer offset %u\n",
p->offset, p->peer_offset);
if (p->cookie_len > 0)
print_cookie("cookie", p->cookie, p->cookie_len);
if (p->peer_cookie_len > 0)
print_cookie("peer cookie", p->peer_cookie, p->peer_cookie_len);
if (p->reorder_timeout != 0)
printf(" reorder timeout: %u\n", p->reorder_timeout);
else
printf("\n");
if (p->send_seq || p->recv_seq) {
printf(" sequence numbering:");
if (p->send_seq)
printf(" send");
if (p->recv_seq)
printf(" recv");
printf("\n");
}
}
static int get_response(struct nlmsghdr *n, void *arg)
{
struct genlmsghdr *ghdr;
struct l2tp_data *data = arg;
struct l2tp_parm *p = &data->config;
struct rtattr *attrs[L2TP_ATTR_MAX + 1];
struct rtattr *nla_stats;
int len;
/* Validate message and parse attributes */
if (n->nlmsg_type == NLMSG_ERROR)
return -EBADMSG;
ghdr = NLMSG_DATA(n);
len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*ghdr));
if (len < 0)
return -1;
parse_rtattr(attrs, L2TP_ATTR_MAX, (void *)ghdr + GENL_HDRLEN, len);
if (attrs[L2TP_ATTR_PW_TYPE])
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])
p->peer_tunnel_id = rta_getattr_u32(attrs[L2TP_ATTR_PEER_CONN_ID]);
if (attrs[L2TP_ATTR_SESSION_ID])
p->session_id = rta_getattr_u32(attrs[L2TP_ATTR_SESSION_ID]);
if (attrs[L2TP_ATTR_PEER_SESSION_ID])
p->peer_session_id = rta_getattr_u32(attrs[L2TP_ATTR_PEER_SESSION_ID]);
if (attrs[L2TP_ATTR_L2SPEC_TYPE])
p->l2spec_type = rta_getattr_u8(attrs[L2TP_ATTR_L2SPEC_TYPE]);
if (attrs[L2TP_ATTR_L2SPEC_LEN])
p->l2spec_len = rta_getattr_u8(attrs[L2TP_ATTR_L2SPEC_LEN]);
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]));
if (attrs[L2TP_ATTR_PEER_COOKIE])
memcpy(p->peer_cookie, RTA_DATA(attrs[L2TP_ATTR_PEER_COOKIE]),
p->peer_cookie_len = RTA_PAYLOAD(attrs[L2TP_ATTR_PEER_COOKIE]));
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]) {
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]) {
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 (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]);
nla_stats = attrs[L2TP_ATTR_STATS];
if (nla_stats) {
struct rtattr *tb[L2TP_ATTR_STATS_MAX + 1];
parse_rtattr_nested(tb, L2TP_ATTR_STATS_MAX, nla_stats);
if (tb[L2TP_ATTR_TX_PACKETS])
data->stats.data_tx_packets = rta_getattr_u64(tb[L2TP_ATTR_TX_PACKETS]);
if (tb[L2TP_ATTR_TX_BYTES])
data->stats.data_tx_bytes = rta_getattr_u64(tb[L2TP_ATTR_TX_BYTES]);
if (tb[L2TP_ATTR_TX_ERRORS])
data->stats.data_tx_errors = rta_getattr_u64(tb[L2TP_ATTR_TX_ERRORS]);
if (tb[L2TP_ATTR_RX_PACKETS])
data->stats.data_rx_packets = rta_getattr_u64(tb[L2TP_ATTR_RX_PACKETS]);
if (tb[L2TP_ATTR_RX_BYTES])
data->stats.data_rx_bytes = rta_getattr_u64(tb[L2TP_ATTR_RX_BYTES]);
if (tb[L2TP_ATTR_RX_ERRORS])
data->stats.data_rx_errors = rta_getattr_u64(tb[L2TP_ATTR_RX_ERRORS]);
if (tb[L2TP_ATTR_RX_SEQ_DISCARDS])
data->stats.data_rx_oos_discards = rta_getattr_u64(tb[L2TP_ATTR_RX_SEQ_DISCARDS]);
if (tb[L2TP_ATTR_RX_OOS_PACKETS])
data->stats.data_rx_oos_packets = rta_getattr_u64(tb[L2TP_ATTR_RX_OOS_PACKETS]);
}
return 0;
}
static int session_nlmsg(const struct sockaddr_nl *who,
struct nlmsghdr *n, void *arg)
{
int ret = get_response(n, arg);
if (ret == 0)
print_session(arg);
return ret;
}
static int get_session(struct l2tp_data *p)
{
GENL_REQUEST(req, 128, genl_family, 0, L2TP_GENL_VERSION,
L2TP_CMD_SESSION_GET,
NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST);
req.n.nlmsg_seq = genl_rth.dump = ++genl_rth.seq;
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);
}
if (rtnl_send(&genl_rth, &req, req.n.nlmsg_len) < 0)
return -2;
if (rtnl_dump_filter(&genl_rth, session_nlmsg, p) < 0) {
fprintf(stderr, "Dump terminated\n");
exit(1);
}
return 0;
}
static int tunnel_nlmsg(const struct sockaddr_nl *who,
struct nlmsghdr *n, void *arg)
{
int ret = get_response(n, arg);
if (ret == 0)
print_tunnel(arg);
return ret;
}
static int get_tunnel(struct l2tp_data *p)
{
GENL_REQUEST(req, 1024, genl_family, 0, L2TP_GENL_VERSION,
L2TP_CMD_TUNNEL_GET,
NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST);
req.n.nlmsg_seq = genl_rth.dump = ++genl_rth.seq;
if (p->config.tunnel_id)
addattr32(&req.n, 1024, L2TP_ATTR_CONN_ID, p->config.tunnel_id);
if (rtnl_send(&genl_rth, &req, req.n.nlmsg_len) < 0)
return -2;
if (rtnl_dump_filter(&genl_rth, tunnel_nlmsg, p) < 0) {
fprintf(stderr, "Dump terminated\n");
exit(1);
}
return 0;
}
/*****************************************************************************
* Command parser
*****************************************************************************/
static void usage(void) __attribute__((noreturn));
static void usage(void)
{
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"
" [ offset OFFSET ] [ peer_offset OFFSET ]\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);
}
static int parse_args(int argc, char **argv, int cmd, struct l2tp_parm *p)
{
memset(p, 0, sizeof(*p));
if (argc == 0)
usage();
/* 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) {
NEXT_ARG();
if (strcmp(*argv, "ip") == 0) {
p->encap = L2TP_ENCAPTYPE_IP;
} else if (strcmp(*argv, "udp") == 0) {
p->encap = L2TP_ENCAPTYPE_UDP;
} else {
fprintf(stderr, "Unknown tunnel encapsulation \"%s\"\n", *argv);
exit(-1);
}
} 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();
if (get_addr(&p->peer_ip, *argv, AF_UNSPEC))
invarg("invalid remote address\n", *argv);
} else if (strcmp(*argv, "local") == 0) {
NEXT_ARG();
if (get_addr(&p->local_ip, *argv, AF_UNSPEC))
invarg("invalid local address\n", *argv);
} 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);
p->tunnel_id = uval;
} 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);
p->peer_tunnel_id = uval;
} 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);
p->session_id = uval;
} 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;
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;
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))
invarg("cookie must be either 8 or 16 hex digits\n", *argv);
p->cookie_len = slen / 2;
if (hex2mem(*argv, p->cookie, p->cookie_len) < 0)
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))
invarg("cookie must be either 8 or 16 hex digits\n", *argv);
p->peer_cookie_len = slen / 2;
if (hex2mem(*argv, p->peer_cookie, p->peer_cookie_len) < 0)
invarg("cookie must be a hex string\n", *argv);
} else if (strcmp(*argv, "l2spec_type") == 0) {
NEXT_ARG();
if (strcasecmp(*argv, "default") == 0) {
p->l2spec_type = L2TP_L2SPECTYPE_DEFAULT;
p->l2spec_len = 4;
} else if (strcasecmp(*argv, "none") == 0) {
p->l2spec_type = L2TP_L2SPECTYPE_NONE;
p->l2spec_len = 0;
} else {
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) {
p->tunnel = 1;
} else if (strcmp(*argv, "session") == 0) {
p->session = 1;
} else if (matches(*argv, "help") == 0) {
usage();
} else {
fprintf(stderr, "Unknown command: %s\n", *argv);
usage();
}
argc--; argv++;
}
return 0;
}
static int do_add(int argc, char **argv)
{
struct l2tp_parm p;
int ret = 0;
if (parse_args(argc, argv, L2TP_ADD, &p) < 0)
return -1;
if (!p.tunnel && !p.session)
missarg("tunnel or session");
if (p.tunnel_id == 0)
missarg("tunnel_id");
/* session_id and peer_session_id must be provided for sessions */
if ((p.session) && (p.peer_session_id == 0))
missarg("peer_session_id");
if ((p.session) && (p.session_id == 0))
missarg("session_id");
/* peer_tunnel_id is needed for tunnels */
if ((p.tunnel) && (p.peer_tunnel_id == 0))
missarg("peer_tunnel_id");
if (p.tunnel) {
if (p.local_ip.family == AF_UNSPEC)
missarg("local");
if (p.peer_ip.family == AF_UNSPEC)
missarg("remote");
if (p.encap == L2TP_ENCAPTYPE_UDP) {
if (p.local_udp_port == 0)
missarg("udp_sport");
if (p.peer_udp_port == 0)
missarg("udp_dport");
}
ret = create_tunnel(&p);
}
if (p.session) {
/* Only ethernet pseudowires supported */
p.pw_type = L2TP_PWTYPE_ETH;
ret = create_session(&p);
}
return ret;
}
static int do_del(int argc, char **argv)
{
struct l2tp_parm p;
if (parse_args(argc, argv, L2TP_DEL, &p) < 0)
return -1;
if (!p.tunnel && !p.session)
missarg("tunnel or session");
if ((p.tunnel) && (p.tunnel_id == 0))
missarg("tunnel_id");
if ((p.session) && (p.session_id == 0))
missarg("session_id");
if (p.session_id)
return delete_session(&p);
else
return delete_tunnel(&p);
return -1;
}
static int do_show(int argc, char **argv)
{
struct l2tp_data data;
struct l2tp_parm *p = &data.config;
if (parse_args(argc, argv, L2TP_GET, p) < 0)
return -1;
if (!p->tunnel && !p->session)
missarg("tunnel or session");
if (p->session)
get_session(&data);
else
get_tunnel(&data);
return 0;
}
int do_ipl2tp(int argc, char **argv)
{
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)
return do_del(argc-1, argv+1);
if (matches(*argv, "show") == 0 ||
matches(*argv, "lst") == 0 ||
matches(*argv, "list") == 0)
return do_show(argc-1, argv+1);
fprintf(stderr,
"Command \"%s\" is unknown, try \"ip l2tp help\".\n", *argv);
exit(-1);
}