Set tun_addaddr ipv agnostic and add support for ipv6
sgsnemu (the only user of this API so far) has been modified to use the new API with in46_addr. FreeBSD code for IPv6 has not been tested. Change-Id: Ie36afe6eaf393855a4a708000ef4ad0192bf4767
This commit is contained in:
parent
02e21af657
commit
f5e40b7011
181
lib/tun.c
181
lib/tun.c
|
@ -295,7 +295,7 @@ int tun_setaddr(struct tun_t *this, struct in46_addr *addr, struct in46_addr *ds
|
|||
}
|
||||
}
|
||||
|
||||
int tun_addaddr(struct tun_t *this,
|
||||
static int tun_addaddr4(struct tun_t *this,
|
||||
struct in_addr *addr,
|
||||
struct in_addr *dstaddr, struct in_addr *netmask)
|
||||
{
|
||||
|
@ -465,6 +465,185 @@ int tun_addaddr(struct tun_t *this,
|
|||
|
||||
}
|
||||
|
||||
static int tun_addaddr6(struct tun_t *this,
|
||||
struct in6_addr *addr,
|
||||
struct in6_addr *dstaddr, int prefixlen)
|
||||
{
|
||||
|
||||
#if defined(__linux__)
|
||||
struct {
|
||||
struct nlmsghdr n;
|
||||
struct ifaddrmsg i;
|
||||
char buf[TUN_NLBUFSIZE];
|
||||
} req;
|
||||
|
||||
struct sockaddr_nl local;
|
||||
socklen_t addr_len;
|
||||
int fd;
|
||||
int status;
|
||||
|
||||
struct sockaddr_nl nladdr;
|
||||
struct iovec iov;
|
||||
struct msghdr msg;
|
||||
|
||||
if (!this->addrs) /* Use ioctl for first addr to make ping work */
|
||||
return tun_setaddr6(this, addr, dstaddr, prefixlen);
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
|
||||
req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
|
||||
req.n.nlmsg_type = RTM_NEWADDR;
|
||||
req.i.ifa_family = AF_INET6;
|
||||
req.i.ifa_prefixlen = 64; /* 64 FOR IPv6 */
|
||||
req.i.ifa_flags = 0;
|
||||
req.i.ifa_scope = RT_SCOPE_HOST; /* TODO or 0 */
|
||||
req.i.ifa_index = if_nametoindex(this->devname);
|
||||
if (!req.i.ifa_index) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "Unable to get ifindex for %s", this->devname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tun_nlattr(&req.n, sizeof(req), IFA_ADDRESS, addr, sizeof(*addr));
|
||||
if (dstaddr)
|
||||
tun_nlattr(&req.n, sizeof(req), IFA_LOCAL, dstaddr, sizeof(*dstaddr));
|
||||
|
||||
if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&local, 0, sizeof(local));
|
||||
local.nl_family = AF_NETLINK;
|
||||
local.nl_groups = 0;
|
||||
|
||||
if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "bind() failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr_len = sizeof(local);
|
||||
if (getsockname(fd, (struct sockaddr *)&local, &addr_len) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno,
|
||||
"getsockname() failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (addr_len != sizeof(local)) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, 0,
|
||||
"Wrong address length %d", addr_len);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (local.nl_family != AF_NETLINK) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, 0,
|
||||
"Wrong address family %d", local.nl_family);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
iov.iov_base = (void *)&req.n;
|
||||
iov.iov_len = req.n.nlmsg_len;
|
||||
|
||||
msg.msg_name = (void *)&nladdr;
|
||||
msg.msg_namelen = sizeof(nladdr);
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
msg.msg_flags = 0;
|
||||
|
||||
memset(&nladdr, 0, sizeof(nladdr));
|
||||
nladdr.nl_family = AF_NETLINK;
|
||||
nladdr.nl_pid = 0;
|
||||
nladdr.nl_groups = 0;
|
||||
|
||||
req.n.nlmsg_seq = 0;
|
||||
req.n.nlmsg_flags |= NLM_F_ACK;
|
||||
|
||||
status = sendmsg(fd, &msg, 0);
|
||||
if (status != req.n.nlmsg_len) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "sendmsg() failed, returned %d", status);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = tun_sifflags(this, IFF_UP | IFF_RUNNING);
|
||||
if (status == -1) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
close(fd);
|
||||
this->addrs++;
|
||||
return 0;
|
||||
|
||||
#elif defined (__FreeBSD__) || defined (__APPLE__)
|
||||
|
||||
int fd;
|
||||
struct ifaliasreq areq;
|
||||
|
||||
/* TODO: Is this needed on FreeBSD? */
|
||||
if (!this->addrs) /* Use ioctl for first addr to make ping work */
|
||||
return tun_setaddr6(this, addr, dstaddr, netmask); /* TODO dstaddr */
|
||||
|
||||
memset(&areq, 0, sizeof(areq));
|
||||
|
||||
/* Set up interface name */
|
||||
strncpy(areq.ifra_name, this->devname, IFNAMSIZ);
|
||||
areq.ifra_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
|
||||
|
||||
((struct sockaddr_in6 *)&areq.ifra_addr)->sin6_family = AF_INET6;
|
||||
((struct sockaddr_in6 *)&areq.ifra_addr)->sin6_len = sizeof(areq.ifra_addr);
|
||||
((struct sockaddr_in6 *)&areq.ifra_addr)->sin6_addr.s6_addr = addr->s6_addr;
|
||||
|
||||
((struct sockaddr_in6 *)&areq.ifra_mask)->sin6_family = AF_INET6;
|
||||
((struct sockaddr_in6 *)&areq.ifra_mask)->sin6_len = sizeof(areq.ifra_mask);
|
||||
((struct sockaddr_in6 *)&areq.ifra_mask)->sin6_addr.s6_addr = netmask->s6_addr;
|
||||
|
||||
/* For some reason FreeBSD uses ifra_broadcast for specifying dstaddr */
|
||||
((struct sockaddr_in6 *)&areq.ifra_broadaddr)->sin6_family = AF_INET6;
|
||||
((struct sockaddr_in6 *)&areq.ifra_broadaddr)->sin6_len = sizeof(areq.ifra_broadaddr);
|
||||
((struct sockaddr_in6 *)&areq.ifra_broadaddr)->sin6_addr.s6_addr = dstaddr->s6_addr;
|
||||
|
||||
/* Create a channel to the NET kernel. */
|
||||
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ioctl(fd, SIOCAIFADDR, (void *)&areq) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno,
|
||||
"ioctl(SIOCAIFADDR) failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
this->addrs++;
|
||||
return 0;
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
int tun_addaddr(struct tun_t *this, struct in46_addr *addr, struct in46_addr *dstaddr, size_t prefixlen)
|
||||
{
|
||||
struct in_addr netmask;
|
||||
switch (addr->len) {
|
||||
case 4:
|
||||
netmask.s_addr = htonl(0xffffffff << (32 - prefixlen));
|
||||
return tun_addaddr4(this, &addr->v4, dstaddr ? &dstaddr->v4 : NULL, &netmask);
|
||||
case 16:
|
||||
return tun_addaddr6(this, &addr->v6, dstaddr ? &dstaddr->v6 : NULL, prefixlen);
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int tun_route(struct tun_t *this,
|
||||
struct in_addr *dst,
|
||||
struct in_addr *gateway, struct in_addr *mask, int delete)
|
||||
|
|
|
@ -80,8 +80,8 @@ extern int tun_free(struct tun_t *tun);
|
|||
extern int tun_decaps(struct tun_t *this);
|
||||
extern int tun_encaps(struct tun_t *tun, void *pack, unsigned len);
|
||||
|
||||
extern int tun_addaddr(struct tun_t *this, struct in_addr *addr,
|
||||
struct in_addr *dstaddr, struct in_addr *netmask);
|
||||
extern int tun_addaddr(struct tun_t *this, struct in46_addr *addr,
|
||||
struct in46_addr *dstaddr, size_t prefixlen);
|
||||
|
||||
extern int tun_setaddr(struct tun_t *this, struct in46_addr *our_adr,
|
||||
struct in46_addr *his_adr, size_t prefixlen);
|
||||
|
|
|
@ -85,7 +85,7 @@ struct {
|
|||
int debug; /* Print debug messages */
|
||||
int createif; /* Create local network interface */
|
||||
char *tun_dev_name;
|
||||
struct in_addr netaddr, destaddr, net; /* Network interface */
|
||||
struct in46_addr netaddr, destaddr, net; /* Network interface */
|
||||
size_t prefixlen;
|
||||
char *ipup, *ipdown; /* Filename of scripts */
|
||||
int defaultroute; /* Set up default route */
|
||||
|
@ -873,23 +873,21 @@ static int process_options(int argc, char **argv)
|
|||
/* net */
|
||||
/* Store net as in_addr net and mask */
|
||||
if (args_info.net_arg) {
|
||||
struct in46_addr in46;
|
||||
if (ippool_aton
|
||||
(&in46, &options.prefixlen, args_info.net_arg, 0)) {
|
||||
(&options.net, &options.prefixlen, args_info.net_arg, 0)) {
|
||||
SYS_ERR(DSGSN, LOGL_ERROR, 0,
|
||||
"Invalid network address: %s!",
|
||||
args_info.net_arg);
|
||||
exit(1);
|
||||
}
|
||||
options.net.s_addr = in46.v4.s_addr;
|
||||
options.netaddr.s_addr = options.net.s_addr;
|
||||
options.destaddr.s_addr = options.net.s_addr;
|
||||
options.netaddr = options.net;
|
||||
options.destaddr = options.net;
|
||||
|
||||
} else {
|
||||
options.net.s_addr = 0;
|
||||
memset(&options.net, 0, sizeof(options.net));
|
||||
options.prefixlen = 0;
|
||||
options.netaddr.s_addr = 0;
|
||||
options.destaddr.s_addr = 0;
|
||||
memset(&options.netaddr, 0, sizeof(options.netaddr));
|
||||
memset(&options.destaddr, 0, sizeof(options.destaddr));
|
||||
}
|
||||
|
||||
/* ipup */
|
||||
|
@ -1427,7 +1425,7 @@ static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
|
|||
break;
|
||||
}
|
||||
|
||||
if ((options.createif) && (!options.net.s_addr)) {
|
||||
if ((options.createif) && (!options.net.len)) {
|
||||
size_t prefixlen = 32;
|
||||
if (addr.len == 16)
|
||||
prefixlen = 64;
|
||||
|
@ -1580,15 +1578,13 @@ int main(int argc, char **argv)
|
|||
maxfd = tun->fd;
|
||||
}
|
||||
|
||||
if ((options.createif) && (options.net.s_addr)) {
|
||||
struct in_addr mask;
|
||||
mask.s_addr = options.prefixlen ? (0xFFFFFFFF >> (32 - options.prefixlen)) : 0;
|
||||
if ((options.createif) && (options.net.len)) {
|
||||
/* printf("Setting up interface and routing\n"); */
|
||||
tun_addaddr(tun, &options.netaddr, &options.destaddr, &mask);
|
||||
tun_addaddr(tun, &options.netaddr, &options.destaddr, options.prefixlen);
|
||||
if (options.defaultroute) {
|
||||
struct in_addr rm;
|
||||
rm.s_addr = 0;
|
||||
tun_addroute(tun, &rm, &options.destaddr, &rm);
|
||||
tun_addroute(tun, &rm, &options.destaddr.v4, &rm);
|
||||
}
|
||||
if (options.ipup)
|
||||
tun_runscript(tun, options.ipup);
|
||||
|
|
Loading…
Reference in New Issue