Message ID | 20100329095737.30460.74520.stgit@bert.katalix.com |
---|---|
State | Changes Requested, archived |
Delegated to: | David Miller |
Headers | show |
Le lundi 29 mars 2010 à 10:57 +0100, James Chapman a écrit : > This patch adds support for static (unmanaged) L2TPv3 tunnels, where > the tunnel socket is created by the kernel rather than being created > by userspace. This means L2TP tunnels and sessions can be created > manually, without needing an L2TP control protocol implemented in > userspace. This might be useful where the user wants a simple ethernet > over IP tunnel. > > A patch to iproute2 adds a new command set under "ip l2tp" to make use > of this feature. This will be submitted separately. > > Signed-off-by: James Chapman <jchapman@katalix.com> > Reviewed-by: Randy Dunlap <randy.dunlap@oracle.com> > --- > net/l2tp/l2tp_core.c | 111 +++++++++++++++++++++++++++++++++++++++++++---- > net/l2tp/l2tp_core.h | 7 +++ > net/l2tp/l2tp_netlink.c | 18 ++++++-- > 3 files changed, 122 insertions(+), 14 deletions(-) > > diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c > index c08c859..1431b39 100644 > --- a/net/l2tp/l2tp_core.c > +++ b/net/l2tp/l2tp_core.c > @@ -1259,6 +1259,78 @@ void l2tp_tunnel_free(struct l2tp_tunnel *tunnel) > } > EXPORT_SYMBOL_GPL(l2tp_tunnel_free); > > +/* Create a socket for the tunnel, if one isn't set up by > + * userspace. This is used for static tunnels where there is no > + * managing L2TP daemon. > + */ > +static int l2tp_tunnel_sock_create(u32 tunnel_id, u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg, struct socket **sockp) > +{ > + int err = -EINVAL; > + struct sockaddr_in udp_addr; > + struct sockaddr_l2tpip ip_addr; > + struct socket *sock; > + > + switch (cfg->encap) { > + case L2TP_ENCAPTYPE_UDP: > + err = sock_create(AF_INET, SOCK_DGRAM, 0, sockp); > + if (err < 0) > + goto out; > + > + sock = *sockp; > + > + memset(&udp_addr, 0, sizeof(udp_addr)); > + udp_addr.sin_family = AF_INET; > + udp_addr.sin_addr = cfg->local_ip; > + udp_addr.sin_port = htons(cfg->local_udp_port); > + err = kernel_bind(sock, (struct sockaddr *) &udp_addr, sizeof(udp_addr)); > + if (err < 0) > + goto out; > + I cant see how you use cfg->use_udp_checksums, something like : ? if (!cfg->use_udp_checksums) sk->sk_no_check = UDP_CSUM_NOXMIT; > + udp_addr.sin_family = AF_INET; > + udp_addr.sin_addr = cfg->peer_ip; > + udp_addr.sin_port = htons(cfg->peer_udp_port); > + err = kernel_connect(sock, (struct sockaddr *) &udp_addr, sizeof(udp_addr), 0); > + if (err < 0) > + goto out; > + > + break; > + case L2TP_ENCAPTYPE_IP: > + err = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_L2TP, sockp); > + if (err < 0) > + goto out; > + > + sock = *sockp; > + > + memset(&ip_addr, 0, sizeof(ip_addr)); > + ip_addr.l2tp_family = AF_INET; > + ip_addr.l2tp_addr = cfg->local_ip; > + ip_addr.l2tp_conn_id = tunnel_id; > + err = kernel_bind(sock, (struct sockaddr *) &ip_addr, sizeof(ip_addr)); > + if (err < 0) > + goto out; > + > + ip_addr.l2tp_family = AF_INET; > + ip_addr.l2tp_addr = cfg->peer_ip; > + ip_addr.l2tp_conn_id = peer_tunnel_id; > + err = kernel_connect(sock, (struct sockaddr *) &ip_addr, sizeof(ip_addr), 0); > + if (err < 0) > + goto out; > + > + break; > + > + default: > + goto out; > + } > + > +out: > + if ((err < 0) && sock) { > + sock_release(sock); > + *sockp = NULL; > + } > + > + return err; > +} > + > int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg, struct l2tp_tunnel **tunnelp) > { > struct l2tp_tunnel *tunnel = NULL; > @@ -1269,14 +1341,21 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 > enum l2tp_encap_type encap = L2TP_ENCAPTYPE_UDP; > > /* Get the tunnel socket from the fd, which was opened by > - * the userspace L2TP daemon. > + * the userspace L2TP daemon. If not specified, create a > + * kernel socket. > */ > - err = -EBADF; > - sock = sockfd_lookup(fd, &err); > - if (!sock) { > - printk(KERN_ERR "tunl %hu: sockfd_lookup(fd=%d) returned %d\n", > - tunnel_id, fd, err); > - goto err; > + if (fd < 0) { > + err = l2tp_tunnel_sock_create(tunnel_id, peer_tunnel_id, cfg, &sock); > + if (err < 0) > + goto err; > + } else { > + err = -EBADF; > + sock = sockfd_lookup(fd, &err); > + if (!sock) { > + printk(KERN_ERR "tunl %hu: sockfd_lookup(fd=%d) returned %d\n", > + tunnel_id, fd, err); > + goto err; > + } > } > > sk = sock->sk; > @@ -1369,7 +1448,10 @@ err: > if (tunnelp) > *tunnelp = tunnel; > > - if (sock) > + /* If tunnel's socket was created by the kernel, it doesn't > + * have a file. > + */ > + if (sock && sock->file) > sockfd_put(sock); > > return err; > @@ -1381,13 +1463,22 @@ EXPORT_SYMBOL_GPL(l2tp_tunnel_create); > int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel) > { > int err = 0; > + struct socket *sock = tunnel->sock ? tunnel->sock->sk_socket : NULL; > > /* Force the tunnel socket to close. This will eventually > * cause the tunnel to be deleted via the normal socket close > * mechanisms when userspace closes the tunnel socket. > */ > - if ((tunnel->sock != NULL) && (tunnel->sock->sk_socket != NULL)) > - err = inet_shutdown(tunnel->sock->sk_socket, 2); > + if (sock != NULL) { > + err = inet_shutdown(sock, 2); > + > + /* If the tunnel's socket was created by the kernel, > + * close the socket here since the socket was not > + * created by userspace. > + */ > + if (sock->file == NULL) > + err = inet_release(sock); > + } > > return err; > } > diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h > index c5f2cc9..7e3387b 100644 > --- a/net/l2tp/l2tp_core.h > +++ b/net/l2tp/l2tp_core.h > @@ -146,6 +146,13 @@ struct l2tp_tunnel_cfg { > int debug; /* bitmask of debug message > * categories */ > enum l2tp_encap_type encap; > + > + /* Used only for kernel-created sockets */ > + struct in_addr local_ip; > + struct in_addr peer_ip; > + u16 local_udp_port; > + u16 peer_udp_port; > + int use_udp_checksums:1; > }; > > struct l2tp_tunnel { > diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c > index e051c13..115ea01 100644 > --- a/net/l2tp/l2tp_netlink.c > +++ b/net/l2tp/l2tp_netlink.c > @@ -129,11 +129,21 @@ static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info > } > cfg.encap = nla_get_u16(info->attrs[L2TP_ATTR_ENCAP_TYPE]); > > - if (!info->attrs[L2TP_ATTR_FD]) { > - ret = -EINVAL; > - goto out; > + fd = -1; > + if (info->attrs[L2TP_ATTR_FD]) { > + fd = nla_get_u32(info->attrs[L2TP_ATTR_FD]); > + } else { > + if (info->attrs[L2TP_ATTR_IP_SADDR]) > + cfg.local_ip.s_addr = nla_get_be32(info->attrs[L2TP_ATTR_IP_SADDR]); > + if (info->attrs[L2TP_ATTR_IP_DADDR]) > + cfg.peer_ip.s_addr = nla_get_be32(info->attrs[L2TP_ATTR_IP_DADDR]); > + if (info->attrs[L2TP_ATTR_UDP_SPORT]) > + cfg.local_udp_port = nla_get_u16(info->attrs[L2TP_ATTR_UDP_SPORT]); > + if (info->attrs[L2TP_ATTR_UDP_DPORT]) > + cfg.peer_udp_port = nla_get_u16(info->attrs[L2TP_ATTR_UDP_DPORT]); > + if (info->attrs[L2TP_ATTR_UDP_CSUM]) > + cfg.use_udp_checksums = nla_get_flag(info->attrs[L2TP_ATTR_UDP_CSUM]); > } > - fd = nla_get_u32(info->attrs[L2TP_ATTR_FD]); > > if (info->attrs[L2TP_ATTR_DEBUG]) > cfg.debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]); > -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Eric Dumazet wrote: > Le lundi 29 mars 2010 à 10:57 +0100, James Chapman a écrit : >> This patch adds support for static (unmanaged) L2TPv3 tunnels, where >> the tunnel socket is created by the kernel rather than being created >> by userspace. This means L2TP tunnels and sessions can be created >> manually, without needing an L2TP control protocol implemented in >> userspace. This might be useful where the user wants a simple ethernet >> over IP tunnel. >> >> A patch to iproute2 adds a new command set under "ip l2tp" to make use >> of this feature. This will be submitted separately. >> >> Signed-off-by: James Chapman <jchapman@katalix.com> >> Reviewed-by: Randy Dunlap <randy.dunlap@oracle.com> >> --- >> net/l2tp/l2tp_core.c | 111 +++++++++++++++++++++++++++++++++++++++++++---- >> net/l2tp/l2tp_core.h | 7 +++ >> net/l2tp/l2tp_netlink.c | 18 ++++++-- >> 3 files changed, 122 insertions(+), 14 deletions(-) >> >> diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c >> index c08c859..1431b39 100644 >> --- a/net/l2tp/l2tp_core.c >> +++ b/net/l2tp/l2tp_core.c >> @@ -1259,6 +1259,78 @@ void l2tp_tunnel_free(struct l2tp_tunnel *tunnel) >> } >> EXPORT_SYMBOL_GPL(l2tp_tunnel_free); >> >> +/* Create a socket for the tunnel, if one isn't set up by >> + * userspace. This is used for static tunnels where there is no >> + * managing L2TP daemon. >> + */ >> +static int l2tp_tunnel_sock_create(u32 tunnel_id, u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg, struct socket **sockp) >> +{ >> + int err = -EINVAL; >> + struct sockaddr_in udp_addr; >> + struct sockaddr_l2tpip ip_addr; >> + struct socket *sock; >> + >> + switch (cfg->encap) { >> + case L2TP_ENCAPTYPE_UDP: >> + err = sock_create(AF_INET, SOCK_DGRAM, 0, sockp); >> + if (err < 0) >> + goto out; >> + >> + sock = *sockp; >> + >> + memset(&udp_addr, 0, sizeof(udp_addr)); >> + udp_addr.sin_family = AF_INET; >> + udp_addr.sin_addr = cfg->local_ip; >> + udp_addr.sin_port = htons(cfg->local_udp_port); >> + err = kernel_bind(sock, (struct sockaddr *) &udp_addr, sizeof(udp_addr)); >> + if (err < 0) >> + goto out; >> + > > I cant see how you use cfg->use_udp_checksums, something like : ? > > if (!cfg->use_udp_checksums) > sk->sk_no_check = UDP_CSUM_NOXMIT; Good point. I forgot to add control of UDP checksums for the kernel socket case. Thanks! > >> + udp_addr.sin_family = AF_INET; >> + udp_addr.sin_addr = cfg->peer_ip; >> + udp_addr.sin_port = htons(cfg->peer_udp_port); >> + err = kernel_connect(sock, (struct sockaddr *) &udp_addr, sizeof(udp_addr), 0); >> + if (err < 0) >> + goto out; >> + >> + break; >> + case L2TP_ENCAPTYPE_IP: >> + err = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_L2TP, sockp); >> + if (err < 0) >> + goto out; >> + >> + sock = *sockp; >> + >> + memset(&ip_addr, 0, sizeof(ip_addr)); >> + ip_addr.l2tp_family = AF_INET; >> + ip_addr.l2tp_addr = cfg->local_ip; >> + ip_addr.l2tp_conn_id = tunnel_id; >> + err = kernel_bind(sock, (struct sockaddr *) &ip_addr, sizeof(ip_addr)); >> + if (err < 0) >> + goto out; >> + >> + ip_addr.l2tp_family = AF_INET; >> + ip_addr.l2tp_addr = cfg->peer_ip; >> + ip_addr.l2tp_conn_id = peer_tunnel_id; >> + err = kernel_connect(sock, (struct sockaddr *) &ip_addr, sizeof(ip_addr), 0); >> + if (err < 0) >> + goto out; >> + >> + break; >> + >> + default: >> + goto out; >> + } >> + >> +out: >> + if ((err < 0) && sock) { >> + sock_release(sock); >> + *sockp = NULL; >> + } >> + >> + return err; >> +} >> + >> int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg, struct l2tp_tunnel **tunnelp) >> { >> struct l2tp_tunnel *tunnel = NULL; >> @@ -1269,14 +1341,21 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 >> enum l2tp_encap_type encap = L2TP_ENCAPTYPE_UDP; >> >> /* Get the tunnel socket from the fd, which was opened by >> - * the userspace L2TP daemon. >> + * the userspace L2TP daemon. If not specified, create a >> + * kernel socket. >> */ >> - err = -EBADF; >> - sock = sockfd_lookup(fd, &err); >> - if (!sock) { >> - printk(KERN_ERR "tunl %hu: sockfd_lookup(fd=%d) returned %d\n", >> - tunnel_id, fd, err); >> - goto err; >> + if (fd < 0) { >> + err = l2tp_tunnel_sock_create(tunnel_id, peer_tunnel_id, cfg, &sock); >> + if (err < 0) >> + goto err; >> + } else { >> + err = -EBADF; >> + sock = sockfd_lookup(fd, &err); >> + if (!sock) { >> + printk(KERN_ERR "tunl %hu: sockfd_lookup(fd=%d) returned %d\n", >> + tunnel_id, fd, err); >> + goto err; >> + } >> } >> >> sk = sock->sk; >> @@ -1369,7 +1448,10 @@ err: >> if (tunnelp) >> *tunnelp = tunnel; >> >> - if (sock) >> + /* If tunnel's socket was created by the kernel, it doesn't >> + * have a file. >> + */ >> + if (sock && sock->file) >> sockfd_put(sock); >> >> return err; >> @@ -1381,13 +1463,22 @@ EXPORT_SYMBOL_GPL(l2tp_tunnel_create); >> int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel) >> { >> int err = 0; >> + struct socket *sock = tunnel->sock ? tunnel->sock->sk_socket : NULL; >> >> /* Force the tunnel socket to close. This will eventually >> * cause the tunnel to be deleted via the normal socket close >> * mechanisms when userspace closes the tunnel socket. >> */ >> - if ((tunnel->sock != NULL) && (tunnel->sock->sk_socket != NULL)) >> - err = inet_shutdown(tunnel->sock->sk_socket, 2); >> + if (sock != NULL) { >> + err = inet_shutdown(sock, 2); >> + >> + /* If the tunnel's socket was created by the kernel, >> + * close the socket here since the socket was not >> + * created by userspace. >> + */ >> + if (sock->file == NULL) >> + err = inet_release(sock); >> + } >> >> return err; >> } >> diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h >> index c5f2cc9..7e3387b 100644 >> --- a/net/l2tp/l2tp_core.h >> +++ b/net/l2tp/l2tp_core.h >> @@ -146,6 +146,13 @@ struct l2tp_tunnel_cfg { >> int debug; /* bitmask of debug message >> * categories */ >> enum l2tp_encap_type encap; >> + >> + /* Used only for kernel-created sockets */ >> + struct in_addr local_ip; >> + struct in_addr peer_ip; >> + u16 local_udp_port; >> + u16 peer_udp_port; >> + int use_udp_checksums:1; >> }; >> >> struct l2tp_tunnel { >> diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c >> index e051c13..115ea01 100644 >> --- a/net/l2tp/l2tp_netlink.c >> +++ b/net/l2tp/l2tp_netlink.c >> @@ -129,11 +129,21 @@ static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info >> } >> cfg.encap = nla_get_u16(info->attrs[L2TP_ATTR_ENCAP_TYPE]); >> >> - if (!info->attrs[L2TP_ATTR_FD]) { >> - ret = -EINVAL; >> - goto out; >> + fd = -1; >> + if (info->attrs[L2TP_ATTR_FD]) { >> + fd = nla_get_u32(info->attrs[L2TP_ATTR_FD]); >> + } else { >> + if (info->attrs[L2TP_ATTR_IP_SADDR]) >> + cfg.local_ip.s_addr = nla_get_be32(info->attrs[L2TP_ATTR_IP_SADDR]); >> + if (info->attrs[L2TP_ATTR_IP_DADDR]) >> + cfg.peer_ip.s_addr = nla_get_be32(info->attrs[L2TP_ATTR_IP_DADDR]); >> + if (info->attrs[L2TP_ATTR_UDP_SPORT]) >> + cfg.local_udp_port = nla_get_u16(info->attrs[L2TP_ATTR_UDP_SPORT]); >> + if (info->attrs[L2TP_ATTR_UDP_DPORT]) >> + cfg.peer_udp_port = nla_get_u16(info->attrs[L2TP_ATTR_UDP_DPORT]); >> + if (info->attrs[L2TP_ATTR_UDP_CSUM]) >> + cfg.use_udp_checksums = nla_get_flag(info->attrs[L2TP_ATTR_UDP_CSUM]); >> } >> - fd = nla_get_u32(info->attrs[L2TP_ATTR_FD]); >> >> if (info->attrs[L2TP_ATTR_DEBUG]) >> cfg.debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]); >> > > >
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index c08c859..1431b39 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -1259,6 +1259,78 @@ void l2tp_tunnel_free(struct l2tp_tunnel *tunnel) } EXPORT_SYMBOL_GPL(l2tp_tunnel_free); +/* Create a socket for the tunnel, if one isn't set up by + * userspace. This is used for static tunnels where there is no + * managing L2TP daemon. + */ +static int l2tp_tunnel_sock_create(u32 tunnel_id, u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg, struct socket **sockp) +{ + int err = -EINVAL; + struct sockaddr_in udp_addr; + struct sockaddr_l2tpip ip_addr; + struct socket *sock; + + switch (cfg->encap) { + case L2TP_ENCAPTYPE_UDP: + err = sock_create(AF_INET, SOCK_DGRAM, 0, sockp); + if (err < 0) + goto out; + + sock = *sockp; + + memset(&udp_addr, 0, sizeof(udp_addr)); + udp_addr.sin_family = AF_INET; + udp_addr.sin_addr = cfg->local_ip; + udp_addr.sin_port = htons(cfg->local_udp_port); + err = kernel_bind(sock, (struct sockaddr *) &udp_addr, sizeof(udp_addr)); + if (err < 0) + goto out; + + udp_addr.sin_family = AF_INET; + udp_addr.sin_addr = cfg->peer_ip; + udp_addr.sin_port = htons(cfg->peer_udp_port); + err = kernel_connect(sock, (struct sockaddr *) &udp_addr, sizeof(udp_addr), 0); + if (err < 0) + goto out; + + break; + case L2TP_ENCAPTYPE_IP: + err = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_L2TP, sockp); + if (err < 0) + goto out; + + sock = *sockp; + + memset(&ip_addr, 0, sizeof(ip_addr)); + ip_addr.l2tp_family = AF_INET; + ip_addr.l2tp_addr = cfg->local_ip; + ip_addr.l2tp_conn_id = tunnel_id; + err = kernel_bind(sock, (struct sockaddr *) &ip_addr, sizeof(ip_addr)); + if (err < 0) + goto out; + + ip_addr.l2tp_family = AF_INET; + ip_addr.l2tp_addr = cfg->peer_ip; + ip_addr.l2tp_conn_id = peer_tunnel_id; + err = kernel_connect(sock, (struct sockaddr *) &ip_addr, sizeof(ip_addr), 0); + if (err < 0) + goto out; + + break; + + default: + goto out; + } + +out: + if ((err < 0) && sock) { + sock_release(sock); + *sockp = NULL; + } + + return err; +} + int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg, struct l2tp_tunnel **tunnelp) { struct l2tp_tunnel *tunnel = NULL; @@ -1269,14 +1341,21 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 enum l2tp_encap_type encap = L2TP_ENCAPTYPE_UDP; /* Get the tunnel socket from the fd, which was opened by - * the userspace L2TP daemon. + * the userspace L2TP daemon. If not specified, create a + * kernel socket. */ - err = -EBADF; - sock = sockfd_lookup(fd, &err); - if (!sock) { - printk(KERN_ERR "tunl %hu: sockfd_lookup(fd=%d) returned %d\n", - tunnel_id, fd, err); - goto err; + if (fd < 0) { + err = l2tp_tunnel_sock_create(tunnel_id, peer_tunnel_id, cfg, &sock); + if (err < 0) + goto err; + } else { + err = -EBADF; + sock = sockfd_lookup(fd, &err); + if (!sock) { + printk(KERN_ERR "tunl %hu: sockfd_lookup(fd=%d) returned %d\n", + tunnel_id, fd, err); + goto err; + } } sk = sock->sk; @@ -1369,7 +1448,10 @@ err: if (tunnelp) *tunnelp = tunnel; - if (sock) + /* If tunnel's socket was created by the kernel, it doesn't + * have a file. + */ + if (sock && sock->file) sockfd_put(sock); return err; @@ -1381,13 +1463,22 @@ EXPORT_SYMBOL_GPL(l2tp_tunnel_create); int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel) { int err = 0; + struct socket *sock = tunnel->sock ? tunnel->sock->sk_socket : NULL; /* Force the tunnel socket to close. This will eventually * cause the tunnel to be deleted via the normal socket close * mechanisms when userspace closes the tunnel socket. */ - if ((tunnel->sock != NULL) && (tunnel->sock->sk_socket != NULL)) - err = inet_shutdown(tunnel->sock->sk_socket, 2); + if (sock != NULL) { + err = inet_shutdown(sock, 2); + + /* If the tunnel's socket was created by the kernel, + * close the socket here since the socket was not + * created by userspace. + */ + if (sock->file == NULL) + err = inet_release(sock); + } return err; } diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h index c5f2cc9..7e3387b 100644 --- a/net/l2tp/l2tp_core.h +++ b/net/l2tp/l2tp_core.h @@ -146,6 +146,13 @@ struct l2tp_tunnel_cfg { int debug; /* bitmask of debug message * categories */ enum l2tp_encap_type encap; + + /* Used only for kernel-created sockets */ + struct in_addr local_ip; + struct in_addr peer_ip; + u16 local_udp_port; + u16 peer_udp_port; + int use_udp_checksums:1; }; struct l2tp_tunnel { diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c index e051c13..115ea01 100644 --- a/net/l2tp/l2tp_netlink.c +++ b/net/l2tp/l2tp_netlink.c @@ -129,11 +129,21 @@ static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info } cfg.encap = nla_get_u16(info->attrs[L2TP_ATTR_ENCAP_TYPE]); - if (!info->attrs[L2TP_ATTR_FD]) { - ret = -EINVAL; - goto out; + fd = -1; + if (info->attrs[L2TP_ATTR_FD]) { + fd = nla_get_u32(info->attrs[L2TP_ATTR_FD]); + } else { + if (info->attrs[L2TP_ATTR_IP_SADDR]) + cfg.local_ip.s_addr = nla_get_be32(info->attrs[L2TP_ATTR_IP_SADDR]); + if (info->attrs[L2TP_ATTR_IP_DADDR]) + cfg.peer_ip.s_addr = nla_get_be32(info->attrs[L2TP_ATTR_IP_DADDR]); + if (info->attrs[L2TP_ATTR_UDP_SPORT]) + cfg.local_udp_port = nla_get_u16(info->attrs[L2TP_ATTR_UDP_SPORT]); + if (info->attrs[L2TP_ATTR_UDP_DPORT]) + cfg.peer_udp_port = nla_get_u16(info->attrs[L2TP_ATTR_UDP_DPORT]); + if (info->attrs[L2TP_ATTR_UDP_CSUM]) + cfg.use_udp_checksums = nla_get_flag(info->attrs[L2TP_ATTR_UDP_CSUM]); } - fd = nla_get_u32(info->attrs[L2TP_ATTR_FD]); if (info->attrs[L2TP_ATTR_DEBUG]) cfg.debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]);