ifconfig: switch IPv4/IPv6 address manipulations to Netlink.
Differential Revision: https://reviews.freebsd.org/D40182
This commit is contained in:
parent
8b709c88e5
commit
4bf44dd73b
@ -55,8 +55,26 @@ static const char rcsid[] =
|
||||
#include "ifconfig.h"
|
||||
#include "ifconfig_netlink.h"
|
||||
|
||||
#ifdef WITHOUT_NETLINK
|
||||
static struct in_aliasreq in_addreq;
|
||||
static struct ifreq in_ridreq;
|
||||
#else
|
||||
struct in_px {
|
||||
struct in_addr addr;
|
||||
int plen;
|
||||
bool addrset;
|
||||
bool maskset;
|
||||
};
|
||||
struct in_pdata {
|
||||
struct in_px addr;
|
||||
struct in_px dst_addr;
|
||||
struct in_px brd_addr;
|
||||
uint32_t flags;
|
||||
uint32_t vhid;
|
||||
};
|
||||
static struct in_pdata in_add, in_del;
|
||||
#endif
|
||||
|
||||
static char addr_buf[NI_MAXHOST]; /*for getnameinfo()*/
|
||||
extern char *f_inet, *f_addr;
|
||||
|
||||
@ -175,6 +193,8 @@ in_status_nl(if_ctx *ctx __unused, if_link_t *link, if_addr_t *ifa)
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef WITHOUT_NETLINK
|
||||
#define SIN(x) ((struct sockaddr_in *) &(x))
|
||||
static struct sockaddr_in *sintab[] = {
|
||||
SIN(in_ridreq.ifr_addr), SIN(in_addreq.ifra_addr),
|
||||
@ -226,14 +246,234 @@ in_getaddr(const char *s, int which)
|
||||
errx(1, "%s: bad value", s);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static struct in_px *sintab_nl[] = {
|
||||
&in_del.addr, /* RIDADDR */
|
||||
&in_add.addr, /* ADDR */
|
||||
NULL, /* MASK */
|
||||
&in_add.dst_addr, /* DSTADDR*/
|
||||
&in_add.brd_addr, /* BRDADDR*/
|
||||
};
|
||||
|
||||
static void
|
||||
in_getip(const char *addr_str, struct in_addr *ip)
|
||||
{
|
||||
struct hostent *hp;
|
||||
struct netent *np;
|
||||
|
||||
if (inet_aton(addr_str, ip))
|
||||
return;
|
||||
if ((hp = gethostbyname(addr_str)) != NULL)
|
||||
bcopy(hp->h_addr, (char *)ip,
|
||||
MIN((size_t)hp->h_length, sizeof(ip)));
|
||||
else if ((np = getnetbyname(addr_str)) != NULL)
|
||||
*ip = inet_makeaddr(np->n_net, INADDR_ANY);
|
||||
else
|
||||
errx(1, "%s: bad value", addr_str);
|
||||
}
|
||||
|
||||
static void
|
||||
in_getaddr(const char *s, int which)
|
||||
{
|
||||
struct in_px *px = sintab_nl[which];
|
||||
|
||||
if (which == MASK) {
|
||||
struct in_px *px_addr = sintab_nl[ADDR];
|
||||
struct in_addr mask = {};
|
||||
|
||||
in_getip(s, &mask);
|
||||
px_addr->plen = __bitcount32(mask.s_addr);
|
||||
px_addr->maskset = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (which == ADDR) {
|
||||
char *p = NULL;
|
||||
|
||||
if((p = strrchr(s, '/')) != NULL) {
|
||||
const char *errstr;
|
||||
/* address is `name/masklen' */
|
||||
int masklen;
|
||||
*p = '\0';
|
||||
if (!isdigit(*(p + 1)))
|
||||
errstr = "invalid";
|
||||
else
|
||||
masklen = (int)strtonum(p + 1, 0, 32, &errstr);
|
||||
if (errstr != NULL) {
|
||||
*p = '/';
|
||||
errx(1, "%s: bad value (width %s)", s, errstr);
|
||||
}
|
||||
px->plen = masklen;
|
||||
px->maskset = true;
|
||||
}
|
||||
}
|
||||
|
||||
in_getip(s, &px->addr);
|
||||
px->addrset = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Deletes the first found IPv4 interface address for the interface.
|
||||
*
|
||||
* This function provides SIOCDIFADDR semantics missing in Netlink.
|
||||
* When no valid IPv4 address is specified (sin_family or sin_len is wrong) to
|
||||
* the SIOCDIFADDR call, it deletes the first found IPv4 address on the interface.
|
||||
* 'ifconfig IFNAME inet addr/prefix' relies on that behavior, as it
|
||||
* executes empty SIOCDIFADDR before adding a new address.
|
||||
*/
|
||||
static int
|
||||
in_delete_first_nl(if_ctx *ctx)
|
||||
{
|
||||
struct nlmsghdr *hdr;
|
||||
struct ifaddrmsg *ifahdr;
|
||||
uint32_t nlmsg_seq;
|
||||
struct in_addr addr;
|
||||
struct snl_writer nw = {};
|
||||
struct snl_errmsg_data e = {};
|
||||
struct snl_state *ss = ctx->io_ss;
|
||||
bool found = false;
|
||||
|
||||
uint32_t ifindex = if_nametoindex_nl(ss, name);
|
||||
if (ifindex == 0) {
|
||||
/* No interface with the desired name, nothing to delete */
|
||||
return (EADDRNOTAVAIL);
|
||||
}
|
||||
|
||||
snl_init_writer(ss, &nw);
|
||||
hdr = snl_create_msg_request(&nw, NL_RTM_GETADDR);
|
||||
hdr->nlmsg_flags |= NLM_F_DUMP;
|
||||
ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg);
|
||||
ifahdr->ifa_family = AF_INET;
|
||||
ifahdr->ifa_index = ifindex;
|
||||
|
||||
if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr))
|
||||
return (EINVAL);
|
||||
|
||||
nlmsg_seq = hdr->nlmsg_seq;
|
||||
while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) {
|
||||
struct snl_parsed_addr attrs = {};
|
||||
if (snl_parse_nlmsg(ss, hdr, &snl_rtm_addr_parser, &attrs)) {
|
||||
addr = satosin(attrs.ifa_local)->sin_addr;
|
||||
ifindex = attrs.ifa_index;
|
||||
found = true;
|
||||
break;
|
||||
} else
|
||||
return (EINVAL);
|
||||
}
|
||||
if (e.error != 0) {
|
||||
if (e.error_str != NULL)
|
||||
warnx("%s(): %s", __func__, e.error_str);
|
||||
return (e.error);
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return (0);
|
||||
|
||||
/* Try to delete the found address */
|
||||
snl_init_writer(ss, &nw);
|
||||
hdr = snl_create_msg_request(&nw, NL_RTM_DELADDR);
|
||||
ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg);
|
||||
ifahdr->ifa_family = AF_INET;
|
||||
ifahdr->ifa_index = ifindex;
|
||||
snl_add_msg_attr_ip4(&nw, IFA_LOCAL, &addr);
|
||||
|
||||
if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr))
|
||||
return (EINVAL);
|
||||
memset(&e, 0, sizeof(e));
|
||||
snl_read_reply_code(ss, hdr->nlmsg_seq, &e);
|
||||
if (e.error_str != NULL)
|
||||
warnx("%s(): %s", __func__, e.error_str);
|
||||
|
||||
return (e.error);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
in_exec_nl(if_ctx *ctx, unsigned long action, void *data)
|
||||
{
|
||||
struct in_pdata *pdata = (struct in_pdata *)data;
|
||||
struct snl_writer nw = {};
|
||||
|
||||
if (action == NL_RTM_DELADDR && !pdata->addr.addrset)
|
||||
return (in_delete_first_nl(ctx));
|
||||
|
||||
snl_init_writer(ctx->io_ss, &nw);
|
||||
struct nlmsghdr *hdr = snl_create_msg_request(&nw, action);
|
||||
struct ifaddrmsg *ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg);
|
||||
|
||||
ifahdr->ifa_family = AF_INET;
|
||||
ifahdr->ifa_prefixlen = pdata->addr.plen;
|
||||
ifahdr->ifa_index = if_nametoindex_nl(ctx->io_ss, name);
|
||||
|
||||
snl_add_msg_attr_ip4(&nw, IFA_LOCAL, &pdata->addr.addr);
|
||||
if (action == NL_RTM_NEWADDR && pdata->dst_addr.addrset)
|
||||
snl_add_msg_attr_ip4(&nw, IFA_ADDRESS, &pdata->dst_addr.addr);
|
||||
if (action == NL_RTM_NEWADDR && pdata->brd_addr.addrset)
|
||||
snl_add_msg_attr_ip4(&nw, IFA_BROADCAST, &pdata->brd_addr.addr);
|
||||
|
||||
int off = snl_add_msg_attr_nested(&nw, IFA_FREEBSD);
|
||||
snl_add_msg_attr_u32(&nw, IFAF_FLAGS, pdata->flags);
|
||||
if (pdata->vhid != 0)
|
||||
snl_add_msg_attr_u32(&nw, IFAF_VHID, pdata->vhid);
|
||||
snl_end_attr_nested(&nw, off);
|
||||
|
||||
if (!snl_finalize_msg(&nw) || !snl_send_message(ctx->io_ss, hdr))
|
||||
return (0);
|
||||
|
||||
struct snl_errmsg_data e = {};
|
||||
snl_read_reply_code(ctx->io_ss, hdr->nlmsg_seq, &e);
|
||||
if (e.error_str != NULL)
|
||||
warnx("%s(): %s", __func__, e.error_str);
|
||||
|
||||
return (e.error);
|
||||
}
|
||||
|
||||
static void
|
||||
in_setdefaultmask_nl(void)
|
||||
{
|
||||
struct in_px *px = sintab_nl[ADDR];
|
||||
|
||||
in_addr_t i = ntohl(px->addr.s_addr);
|
||||
|
||||
/*
|
||||
* If netmask isn't supplied, use historical default.
|
||||
* This is deprecated for interfaces other than loopback
|
||||
* or point-to-point; warn in other cases. In the future
|
||||
* we should return an error rather than warning.
|
||||
*/
|
||||
if (IN_CLASSA(i))
|
||||
px->plen = IN_CLASSA_NSHIFT;
|
||||
else if (IN_CLASSB(i))
|
||||
px->plen = IN_CLASSB_NSHIFT;
|
||||
else
|
||||
px->plen = IN_CLASSC_NSHIFT;
|
||||
px->maskset = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
warn_nomask(int ifflags)
|
||||
{
|
||||
if ((ifflags & (IFF_POINTOPOINT | IFF_LOOPBACK)) == 0) {
|
||||
warnx("WARNING: setting interface address without mask "
|
||||
"is deprecated,\ndefault mask may not be correct.");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
in_postproc(if_ctx *ctx __unused, int newaddr, int ifflags)
|
||||
{
|
||||
if (sintab[ADDR]->sin_len != 0 && sintab[MASK]->sin_len == 0 &&
|
||||
newaddr && (ifflags & (IFF_POINTOPOINT | IFF_LOOPBACK)) == 0) {
|
||||
warnx("WARNING: setting interface address without mask "
|
||||
"is deprecated,\ndefault mask may not be correct.");
|
||||
#ifdef WITHOUT_NETLINK
|
||||
if (sintab[ADDR]->sin_len != 0 && sintab[MASK]->sin_len == 0 && newaddr) {
|
||||
warn_nomask(ifflags);
|
||||
}
|
||||
#else
|
||||
if (sintab_nl[ADDR]->addrset && !sintab_nl[ADDR]->maskset && newaddr) {
|
||||
warn_nomask(ifflags);
|
||||
in_setdefaultmask_nl();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
@ -281,10 +521,13 @@ in_set_tunnel(int s, struct addrinfo *srcres, struct addrinfo *dstres)
|
||||
static void
|
||||
in_set_vhid(int vhid)
|
||||
{
|
||||
#ifdef WITHOUT_NETLINK
|
||||
in_addreq.ifra_vhid = vhid;
|
||||
#else
|
||||
in_add.vhid = (uint32_t)vhid;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static struct afswtch af_inet = {
|
||||
.af_name = "inet",
|
||||
.af_af = AF_INET,
|
||||
@ -298,10 +541,19 @@ static struct afswtch af_inet = {
|
||||
.af_status_tunnel = in_status_tunnel,
|
||||
.af_settunnel = in_set_tunnel,
|
||||
.af_setvhid = in_set_vhid,
|
||||
#ifdef WITHOUT_NETLINK
|
||||
.af_difaddr = SIOCDIFADDR,
|
||||
.af_aifaddr = SIOCAIFADDR,
|
||||
.af_ridreq = &in_ridreq,
|
||||
.af_addreq = &in_addreq,
|
||||
.af_exec = af_exec_ioctl,
|
||||
#else
|
||||
.af_difaddr = NL_RTM_DELADDR,
|
||||
.af_aifaddr = NL_RTM_NEWADDR,
|
||||
.af_ridreq = &in_del,
|
||||
.af_addreq = &in_add,
|
||||
.af_exec = in_exec_nl,
|
||||
#endif
|
||||
};
|
||||
|
||||
static __constructor void
|
||||
|
@ -59,10 +59,30 @@ static const char rcsid[] =
|
||||
#include "ifconfig.h"
|
||||
#include "ifconfig_netlink.h"
|
||||
|
||||
#ifndef WITHOUT_NETLINK
|
||||
struct in6_px {
|
||||
struct in6_addr addr;
|
||||
int plen;
|
||||
bool set;
|
||||
};
|
||||
struct in6_pdata {
|
||||
struct in6_px addr;
|
||||
struct in6_px dst_addr;
|
||||
struct in6_addrlifetime lifetime;
|
||||
uint32_t flags;
|
||||
uint32_t vhid;
|
||||
};
|
||||
|
||||
static struct in6_pdata in6_del;
|
||||
static struct in6_pdata in6_add = {
|
||||
.lifetime = { 0, 0, ND6_INFINITE_LIFETIME, ND6_INFINITE_LIFETIME },
|
||||
};
|
||||
#else
|
||||
static struct in6_ifreq in6_ridreq;
|
||||
static struct in6_aliasreq in6_addreq =
|
||||
{ .ifra_flags = 0,
|
||||
.ifra_lifetime = { 0, 0, ND6_INFINITE_LIFETIME, ND6_INFINITE_LIFETIME } };
|
||||
#endif
|
||||
static int ip6lifetime;
|
||||
|
||||
#ifdef WITHOUT_NETLINK
|
||||
@ -81,10 +101,18 @@ static char addr_buf[NI_MAXHOST]; /*for getnameinfo()*/
|
||||
static void
|
||||
setifprefixlen(if_ctx *ctx, const char *addr, int dummy __unused)
|
||||
{
|
||||
#ifdef WITHOUT_NETLINK
|
||||
const struct afswtch *afp = ctx->afp;
|
||||
|
||||
if (afp->af_getprefix != NULL)
|
||||
afp->af_getprefix(addr, MASK);
|
||||
#else
|
||||
int plen = strtol(addr, NULL, 10);
|
||||
|
||||
if ((plen < 0) || (plen > 128))
|
||||
errx(1, "%s: bad value", addr);
|
||||
in6_add.addr.plen = plen;
|
||||
#endif
|
||||
explicit_prefix = 1;
|
||||
}
|
||||
|
||||
@ -96,10 +124,17 @@ setip6flags(if_ctx *ctx, const char *dummyaddr __unused, int flag)
|
||||
if (afp->af_af != AF_INET6)
|
||||
err(1, "address flags can be set only for inet6 addresses");
|
||||
|
||||
#ifdef WITHOUT_NETLINK
|
||||
if (flag < 0)
|
||||
in6_addreq.ifra_flags &= ~(-flag);
|
||||
else
|
||||
in6_addreq.ifra_flags |= flag;
|
||||
#else
|
||||
if (flag < 0)
|
||||
in6_add.flags &= ~(-flag);
|
||||
else
|
||||
in6_add.flags |= flag;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
@ -109,6 +144,11 @@ setip6lifetime(if_ctx *ctx, const char *cmd, const char *val)
|
||||
struct timespec now;
|
||||
time_t newval;
|
||||
char *ep;
|
||||
#ifdef WITHOUT_NETLINK
|
||||
struct in6_addrlifetime *lifetime = &in6_addreq.ifra_lifetime;
|
||||
#else
|
||||
struct in6_addrlifetime *lifetime = &in6_add.lifetime;
|
||||
#endif
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC_FAST, &now);
|
||||
newval = (time_t)strtoul(val, &ep, 0);
|
||||
@ -117,11 +157,11 @@ setip6lifetime(if_ctx *ctx, const char *cmd, const char *val)
|
||||
if (afp->af_af != AF_INET6)
|
||||
errx(1, "%s not allowed for the AF", cmd);
|
||||
if (strcmp(cmd, "vltime") == 0) {
|
||||
in6_addreq.ifra_lifetime.ia6t_expire = now.tv_sec + newval;
|
||||
in6_addreq.ifra_lifetime.ia6t_vltime = newval;
|
||||
lifetime->ia6t_expire = now.tv_sec + newval;
|
||||
lifetime->ia6t_vltime = newval;
|
||||
} else if (strcmp(cmd, "pltime") == 0) {
|
||||
in6_addreq.ifra_lifetime.ia6t_preferred = now.tv_sec + newval;
|
||||
in6_addreq.ifra_lifetime.ia6t_pltime = newval;
|
||||
lifetime->ia6t_preferred = now.tv_sec + newval;
|
||||
lifetime->ia6t_pltime = newval;
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,7 +188,11 @@ setip6eui64(if_ctx *ctx, const char *cmd, int dummy __unused)
|
||||
|
||||
if (afp->af_af != AF_INET6)
|
||||
errx(EXIT_FAILURE, "%s not allowed for the AF", cmd);
|
||||
#ifdef WITHOUT_NETLINK
|
||||
in6 = (struct in6_addr *)&in6_addreq.ifra_addr.sin6_addr;
|
||||
#else
|
||||
in6 = &in6_add.addr.addr;
|
||||
#endif
|
||||
if (memcmp(&in6addr_any.s6_addr[8], &in6->s6_addr[8], 8) != 0)
|
||||
errx(EXIT_FAILURE, "interface index is already filled");
|
||||
if (getifaddrs(&ifap) != 0)
|
||||
@ -369,8 +413,92 @@ in6_status_nl(if_ctx *ctx __unused, if_link_t *link, if_addr_t *ifa)
|
||||
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
static struct in6_px *sin6tab_nl[] = {
|
||||
&in6_del.addr, /* RIDADDR */
|
||||
&in6_add.addr, /* ADDR */
|
||||
NULL, /* MASK */
|
||||
&in6_add.dst_addr, /* DSTADDR*/
|
||||
};
|
||||
|
||||
static void
|
||||
in6_getaddr(const char *addr_str, int which)
|
||||
{
|
||||
struct in6_px *px = sin6tab_nl[which];
|
||||
|
||||
newaddr &= 1;
|
||||
|
||||
px->set = true;
|
||||
px->plen = 128;
|
||||
if (which == ADDR) {
|
||||
char *p = NULL;
|
||||
if((p = strrchr(addr_str, '/')) != NULL) {
|
||||
*p = '\0';
|
||||
int plen = strtol(p + 1, NULL, 10);
|
||||
if (plen < 0 || plen > 128)
|
||||
errx(1, "%s: bad value", p + 1);
|
||||
px->plen = plen;
|
||||
explicit_prefix = 1;
|
||||
}
|
||||
}
|
||||
|
||||
struct addrinfo hints = { .ai_family = AF_INET6 };
|
||||
struct addrinfo *res;
|
||||
|
||||
int error = getaddrinfo(addr_str, NULL, &hints, &res);
|
||||
if (error != 0) {
|
||||
if (inet_pton(AF_INET6, addr_str, &px->addr) != 1)
|
||||
errx(1, "%s: bad value", addr_str);
|
||||
} else {
|
||||
struct sockaddr_in6 *sin6;
|
||||
|
||||
sin6 = (struct sockaddr_in6 *)(void *)res->ai_addr;
|
||||
px->addr = sin6->sin6_addr;
|
||||
freeaddrinfo(res);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
in6_exec_nl(if_ctx *ctx, unsigned long action, void *data)
|
||||
{
|
||||
struct in6_pdata *pdata = (struct in6_pdata *)data;
|
||||
struct snl_writer nw = {};
|
||||
|
||||
snl_init_writer(ctx->io_ss, &nw);
|
||||
struct nlmsghdr *hdr = snl_create_msg_request(&nw, action);
|
||||
struct ifaddrmsg *ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg);
|
||||
|
||||
ifahdr->ifa_family = AF_INET6;
|
||||
ifahdr->ifa_prefixlen = pdata->addr.plen;
|
||||
ifahdr->ifa_index = if_nametoindex_nl(ctx->io_ss, name);
|
||||
|
||||
snl_add_msg_attr_ip6(&nw, IFA_LOCAL, &pdata->addr.addr);
|
||||
if (action == NL_RTM_NEWADDR && pdata->dst_addr.set)
|
||||
snl_add_msg_attr_ip6(&nw, IFA_ADDRESS, &pdata->dst_addr.addr);
|
||||
|
||||
struct ifa_cacheinfo ci = {
|
||||
.ifa_prefered = pdata->lifetime.ia6t_pltime,
|
||||
.ifa_valid = pdata->lifetime.ia6t_vltime,
|
||||
};
|
||||
snl_add_msg_attr(&nw, IFA_CACHEINFO, sizeof(ci), &ci);
|
||||
|
||||
int off = snl_add_msg_attr_nested(&nw, IFA_FREEBSD);
|
||||
snl_add_msg_attr_u32(&nw, IFAF_FLAGS, pdata->flags);
|
||||
if (pdata->vhid != 0)
|
||||
snl_add_msg_attr_u32(&nw, IFAF_VHID, pdata->vhid);
|
||||
snl_end_attr_nested(&nw, off);
|
||||
|
||||
if (!snl_finalize_msg(&nw) || !snl_send_message(ctx->io_ss, hdr))
|
||||
return (0);
|
||||
|
||||
struct snl_errmsg_data e = {};
|
||||
snl_read_reply_code(ctx->io_ss, hdr->nlmsg_seq, &e);
|
||||
|
||||
return (e.error);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WITHOUT_NETLINK
|
||||
static struct sockaddr_in6 *sin6tab[] = {
|
||||
&in6_ridreq.ifr_addr, &in6_addreq.ifra_addr,
|
||||
&in6_addreq.ifra_prefixmask, &in6_addreq.ifra_dstaddr
|
||||
@ -434,7 +562,6 @@ in6_getaddr(const char *s, int which)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WITHOUT_NETLINK
|
||||
static int
|
||||
prefix(void *val, int size)
|
||||
{
|
||||
@ -554,7 +681,11 @@ in6_set_tunnel(int s, struct addrinfo *srcres, struct addrinfo *dstres)
|
||||
static void
|
||||
in6_set_vhid(int vhid)
|
||||
{
|
||||
#ifdef WITHOUT_NETLINK
|
||||
in6_addreq.ifra_vhid = vhid;
|
||||
#else
|
||||
in6_add.vhid = (uint32_t)vhid;
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct cmd inet6_cmds[] = {
|
||||
@ -602,16 +733,27 @@ static struct afswtch af_inet6 = {
|
||||
.af_status = in6_status_nl,
|
||||
#endif
|
||||
.af_getaddr = in6_getaddr,
|
||||
#ifdef WITHOUT_NETLINK
|
||||
.af_getprefix = in6_getprefix,
|
||||
#endif
|
||||
.af_other_status = nd6_status,
|
||||
.af_postproc = in6_postproc,
|
||||
.af_status_tunnel = in6_status_tunnel,
|
||||
.af_settunnel = in6_set_tunnel,
|
||||
.af_setvhid = in6_set_vhid,
|
||||
#ifdef WITHOUT_NETLINK
|
||||
.af_difaddr = SIOCDIFADDR_IN6,
|
||||
.af_aifaddr = SIOCAIFADDR_IN6,
|
||||
.af_ridreq = &in6_addreq,
|
||||
.af_addreq = &in6_addreq,
|
||||
.af_exec = af_exec_ioctl,
|
||||
#else
|
||||
.af_difaddr = NL_RTM_DELADDR,
|
||||
.af_aifaddr = NL_RTM_NEWADDR,
|
||||
.af_ridreq = &in6_add,
|
||||
.af_addreq = &in6_add,
|
||||
.af_exec = in6_exec_nl,
|
||||
#endif
|
||||
};
|
||||
|
||||
static void
|
||||
|
@ -228,6 +228,7 @@ static struct afswtch af_link = {
|
||||
.af_getaddr = link_getaddr,
|
||||
.af_aifaddr = SIOCSIFLLADDR,
|
||||
.af_addreq = &link_ridreq,
|
||||
.af_exec = af_exec_ioctl,
|
||||
};
|
||||
static struct afswtch af_ether = {
|
||||
.af_name = "ether",
|
||||
@ -240,6 +241,7 @@ static struct afswtch af_ether = {
|
||||
.af_getaddr = link_getaddr,
|
||||
.af_aifaddr = SIOCSIFLLADDR,
|
||||
.af_addreq = &link_ridreq,
|
||||
.af_exec = af_exec_ioctl,
|
||||
};
|
||||
static struct afswtch af_lladdr = {
|
||||
.af_name = "lladdr",
|
||||
@ -252,6 +254,7 @@ static struct afswtch af_lladdr = {
|
||||
.af_getaddr = link_getaddr,
|
||||
.af_aifaddr = SIOCSIFLLADDR,
|
||||
.af_addreq = &link_ridreq,
|
||||
.af_exec = af_exec_ioctl,
|
||||
};
|
||||
|
||||
static __constructor void
|
||||
|
@ -119,6 +119,7 @@ static void status(struct ifconfig_args *args, const struct sockaddr_dl *sdl,
|
||||
struct ifaddrs *ifa);
|
||||
#endif
|
||||
static _Noreturn void usage(void);
|
||||
static void Perrorc(const char *cmd, int error);
|
||||
|
||||
static int getifflags(const char *ifname, int us, bool err_ok);
|
||||
|
||||
@ -560,6 +561,34 @@ args_parse(struct ifconfig_args *args, int argc, char *argv[])
|
||||
verbose = args->verbose;
|
||||
}
|
||||
|
||||
static int
|
||||
ifconfig_wrapper(struct ifconfig_args *args, int iscreate,
|
||||
const struct afswtch *uafp)
|
||||
{
|
||||
#ifdef WITHOUT_NETLINK
|
||||
struct ifconfig_context ctx = {
|
||||
.args = args,
|
||||
.io_s = -1,
|
||||
};
|
||||
|
||||
return (ifconfig(&ctx, iscreate, uafp));
|
||||
#else
|
||||
return (ifconfig_wrapper_nl(args, iscreate, uafp));
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool
|
||||
isargcreate(const char *arg)
|
||||
{
|
||||
if (arg == NULL)
|
||||
return (false);
|
||||
|
||||
if (strcmp(arg, "create") == 0 || strcmp(arg, "plumb") == 0)
|
||||
return (true);
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
int
|
||||
main(int ac, char *av[])
|
||||
{
|
||||
@ -613,13 +642,12 @@ main(int ac, char *av[])
|
||||
* right here as we would otherwise fail when trying
|
||||
* to find the interface.
|
||||
*/
|
||||
if (arg != NULL && (strcmp(arg, "create") == 0 ||
|
||||
strcmp(arg, "plumb") == 0)) {
|
||||
if (isargcreate(arg)) {
|
||||
iflen = strlcpy(name, args->ifname, sizeof(name));
|
||||
if (iflen >= sizeof(name))
|
||||
errx(1, "%s: cloning name too long",
|
||||
args->ifname);
|
||||
ifconfig(args->argc, args->argv, 1, NULL);
|
||||
ifconfig_wrapper(args, 1, NULL);
|
||||
exit(exit_code);
|
||||
}
|
||||
#ifdef JAIL
|
||||
@ -633,7 +661,7 @@ main(int ac, char *av[])
|
||||
if (iflen >= sizeof(name))
|
||||
errx(1, "%s: interface name too long",
|
||||
args->ifname);
|
||||
ifconfig(args->argc, args->argv, 0, NULL);
|
||||
ifconfig_wrapper(args, 0, NULL);
|
||||
exit(exit_code);
|
||||
}
|
||||
#endif
|
||||
@ -643,8 +671,7 @@ main(int ac, char *av[])
|
||||
* Do not allow use `create` command as hostname if
|
||||
* address family is not specified.
|
||||
*/
|
||||
if (arg != NULL && (strcmp(arg, "create") == 0 ||
|
||||
strcmp(arg, "plumb") == 0)) {
|
||||
if (isargcreate(arg)) {
|
||||
if (args->argc == 1)
|
||||
errx(1, "interface %s already exists",
|
||||
args->ifname);
|
||||
@ -674,7 +701,7 @@ main(int ac, char *av[])
|
||||
if (!(((flags & IFF_CANTCONFIG) != 0) ||
|
||||
(args->downonly && (flags & IFF_UP) != 0) ||
|
||||
(args->uponly && (flags & IFF_UP) == 0)))
|
||||
ifconfig(args->argc, args->argv, 0, args->afp);
|
||||
ifconfig_wrapper(args, 0, args->afp);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
@ -802,7 +829,7 @@ list_interfaces_ioctl(struct ifconfig_args *args)
|
||||
ifindex++;
|
||||
|
||||
if (args->argc > 0)
|
||||
ifconfig(args->argc, args->argv, 0, args->afp);
|
||||
ifconfig_wrapper(args, 0, args->afp);
|
||||
else
|
||||
status(args, sdl, ifa);
|
||||
}
|
||||
@ -1001,31 +1028,42 @@ static void setifdstaddr(if_ctx *ctx, const char *addr, int param __unused);
|
||||
static const struct cmd setifdstaddr_cmd =
|
||||
DEF_CMD("ifdstaddr", 0, setifdstaddr);
|
||||
|
||||
static void
|
||||
delifaddr(int s, const struct afswtch *afp)
|
||||
int
|
||||
af_exec_ioctl(if_ctx *ctx, unsigned long action, void *data)
|
||||
{
|
||||
if (afp->af_ridreq == NULL || afp->af_difaddr == 0) {
|
||||
struct ifreq *req = (struct ifreq *)data;
|
||||
|
||||
strlcpy(req->ifr_name, name, sizeof(req->ifr_name));
|
||||
if (ioctl_ctx(ctx, action, req) == 0)
|
||||
return (0);
|
||||
return (errno);
|
||||
}
|
||||
|
||||
static void
|
||||
delifaddr(if_ctx *ctx, const struct afswtch *afp)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (afp->af_exec == NULL) {
|
||||
warnx("interface %s cannot change %s addresses!",
|
||||
name, afp->af_name);
|
||||
clearaddr = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
strlcpy(((struct ifreq *)afp->af_ridreq)->ifr_name, name,
|
||||
sizeof ifr.ifr_name);
|
||||
int ret = ioctl(s, afp->af_difaddr, afp->af_ridreq);
|
||||
if (ret < 0) {
|
||||
if (errno == EADDRNOTAVAIL && (doalias >= 0)) {
|
||||
error = afp->af_exec(ctx, afp->af_difaddr, afp->af_ridreq);
|
||||
if (error != 0) {
|
||||
if (error == EADDRNOTAVAIL && (doalias >= 0)) {
|
||||
/* means no previous address for interface */
|
||||
} else
|
||||
Perror("ioctl (SIOCDIFADDR)");
|
||||
Perrorc("ioctl (SIOCDIFADDR)", error);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
addifaddr(int s, const struct afswtch *afp)
|
||||
addifaddr(if_ctx *ctx, const struct afswtch *afp)
|
||||
{
|
||||
if (afp->af_addreq == NULL || afp->af_aifaddr == 0) {
|
||||
if (afp->af_exec == NULL) {
|
||||
warnx("interface %s cannot change %s addresses!",
|
||||
name, afp->af_name);
|
||||
newaddr = 0;
|
||||
@ -1033,21 +1071,26 @@ addifaddr(int s, const struct afswtch *afp)
|
||||
}
|
||||
|
||||
if (setaddr || setmask) {
|
||||
strlcpy(((struct ifreq *)afp->af_addreq)->ifr_name, name,
|
||||
sizeof ifr.ifr_name);
|
||||
if (ioctl(s, afp->af_aifaddr, afp->af_addreq) < 0)
|
||||
Perror("ioctl (SIOCAIFADDR)");
|
||||
int error = afp->af_exec(ctx, afp->af_aifaddr, afp->af_addreq);
|
||||
if (error != 0)
|
||||
Perrorc("ioctl (SIOCAIFADDR)", error);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
ifconfig(int argc, char *const *argv, int iscreate, const struct afswtch *uafp)
|
||||
ifconfig(if_ctx *orig_ctx, int iscreate, const struct afswtch *uafp)
|
||||
{
|
||||
struct ifconfig_context ctx = {};
|
||||
const struct afswtch *afp, *nafp;
|
||||
const struct cmd *p;
|
||||
struct callback *cb;
|
||||
int s;
|
||||
int argc = orig_ctx->args->argc;
|
||||
char *const *argv = orig_ctx->args->argv;
|
||||
struct ifconfig_context _ctx = {
|
||||
.args = orig_ctx->args,
|
||||
.io_ss = orig_ctx->io_ss,
|
||||
};
|
||||
struct ifconfig_context *ctx = &_ctx;
|
||||
|
||||
strlcpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
|
||||
afp = NULL;
|
||||
@ -1085,8 +1128,8 @@ ifconfig(int argc, char *const *argv, int iscreate, const struct afswtch *uafp)
|
||||
(s = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0))
|
||||
err(1, "socket(family %u,SOCK_DGRAM)", ifr.ifr_addr.sa_family);
|
||||
|
||||
ctx.io_s = s;
|
||||
ctx.afp = afp;
|
||||
ctx->io_s = s;
|
||||
ctx->afp = afp;
|
||||
|
||||
while (argc > 0) {
|
||||
p = cmd_lookup(*argv, iscreate);
|
||||
@ -1132,22 +1175,22 @@ ifconfig(int argc, char *const *argv, int iscreate, const struct afswtch *uafp)
|
||||
if (argv[1] == NULL)
|
||||
errx(1, "'%s' requires argument",
|
||||
p->c_name);
|
||||
p->c_u.c_func(&ctx, argv[1], 0);
|
||||
p->c_u.c_func(ctx, argv[1], 0);
|
||||
argc--, argv++;
|
||||
} else if (p->c_parameter == OPTARG && p->c_u.c_func) {
|
||||
p->c_u.c_func(&ctx, argv[1], 0);
|
||||
p->c_u.c_func(ctx, argv[1], 0);
|
||||
if (argv[1] != NULL)
|
||||
argc--, argv++;
|
||||
} else if (p->c_parameter == NEXTARG2 && p->c_u.c_func2) {
|
||||
if (argc < 3)
|
||||
errx(1, "'%s' requires 2 arguments",
|
||||
p->c_name);
|
||||
p->c_u.c_func2(&ctx, argv[1], argv[2]);
|
||||
p->c_u.c_func2(ctx, argv[1], argv[2]);
|
||||
argc -= 2, argv += 2;
|
||||
} else if (p->c_parameter == SPARAM && p->c_u.c_func3) {
|
||||
p->c_u.c_func3(&ctx, *argv, p->c_sparameter);
|
||||
p->c_u.c_func3(ctx, *argv, p->c_sparameter);
|
||||
} else if (p->c_u.c_func)
|
||||
p->c_u.c_func(&ctx, *argv, p->c_parameter);
|
||||
p->c_u.c_func(ctx, *argv, p->c_parameter);
|
||||
argc--, argv++;
|
||||
}
|
||||
|
||||
@ -1155,7 +1198,7 @@ ifconfig(int argc, char *const *argv, int iscreate, const struct afswtch *uafp)
|
||||
* Do any post argument processing required by the address family.
|
||||
*/
|
||||
if (afp->af_postproc != NULL)
|
||||
afp->af_postproc(&ctx, newaddr, getifflags(name, s, true));
|
||||
afp->af_postproc(ctx, newaddr, getifflags(name, s, true));
|
||||
/*
|
||||
* Do deferred callbacks registered while processing
|
||||
* command-line arguments.
|
||||
@ -1166,9 +1209,9 @@ ifconfig(int argc, char *const *argv, int iscreate, const struct afswtch *uafp)
|
||||
* Do deferred operations.
|
||||
*/
|
||||
if (clearaddr)
|
||||
delifaddr(s, afp);
|
||||
delifaddr(ctx, afp);
|
||||
if (newaddr)
|
||||
addifaddr(s, afp);
|
||||
addifaddr(ctx, afp);
|
||||
|
||||
close(s);
|
||||
return(0);
|
||||
@ -1276,7 +1319,7 @@ setifbroadaddr(if_ctx *ctx, const char *addr, int dummy __unused)
|
||||
const struct afswtch *afp = ctx->afp;
|
||||
|
||||
if (afp->af_getaddr != NULL)
|
||||
afp->af_getaddr(addr, DSTADDR);
|
||||
afp->af_getaddr(addr, BRDADDR);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1749,8 +1792,8 @@ tunnel_status(int s)
|
||||
af_all_tunnel_status(s);
|
||||
}
|
||||
|
||||
void
|
||||
Perror(const char *cmd)
|
||||
static void
|
||||
Perrorc(const char *cmd, int error)
|
||||
{
|
||||
switch (errno) {
|
||||
|
||||
@ -1763,10 +1806,16 @@ Perror(const char *cmd)
|
||||
break;
|
||||
|
||||
default:
|
||||
err(1, "%s", cmd);
|
||||
errc(1, error, "%s", cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Perror(const char *cmd)
|
||||
{
|
||||
Perrorc(cmd, errno);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print a value a la the %b format of the kernel's printf
|
||||
*/
|
||||
|
@ -152,10 +152,15 @@ struct ifaddrs;
|
||||
struct addrinfo;
|
||||
|
||||
enum {
|
||||
RIDADDR,
|
||||
ADDR,
|
||||
MASK,
|
||||
DSTADDR,
|
||||
RIDADDR = 0,
|
||||
ADDR = 1,
|
||||
MASK = 2,
|
||||
DSTADDR = 3,
|
||||
#ifdef WITHOUT_NETLINK
|
||||
BRDADDR = 3,
|
||||
#else
|
||||
BRDADDR = 4,
|
||||
#endif
|
||||
};
|
||||
|
||||
struct snl_parsed_addr;
|
||||
@ -168,6 +173,7 @@ typedef void af_status_nl_f(if_ctx *ctx, if_link_t *link, if_addr_t *ifa);
|
||||
typedef void af_status_f(if_ctx *ctx, const struct ifaddrs *);
|
||||
typedef void af_other_status_f(if_ctx *ctx);
|
||||
typedef void af_postproc_f(if_ctx *ctx, int newaddr, int ifflags);
|
||||
typedef int af_exec_f(if_ctx *ctx, unsigned long action, void *data);
|
||||
|
||||
struct afswtch {
|
||||
const char *af_name; /* as given on cmd line, e.g. "inet" */
|
||||
@ -192,6 +198,7 @@ struct afswtch {
|
||||
void (*af_getprefix)(const char *, int);
|
||||
af_postproc_f *af_postproc;
|
||||
af_setvhid_f *af_setvhid; /* Set CARP vhid for an address */
|
||||
af_exec_f *af_exec; /* Handler to interact with kernel */
|
||||
u_long af_difaddr; /* set dst if address ioctl */
|
||||
u_long af_aifaddr; /* set if address ioctl */
|
||||
void *af_ridreq; /* */
|
||||
@ -204,6 +211,7 @@ struct afswtch {
|
||||
struct addrinfo *dstres);
|
||||
};
|
||||
void af_register(struct afswtch *);
|
||||
int af_exec_ioctl(if_ctx *ctx, unsigned long action, void *data);
|
||||
|
||||
struct ifconfig_args {
|
||||
bool all; /* Match everything */
|
||||
@ -262,7 +270,7 @@ void sfp_status(int s, struct ifreq *ifr, int verbose);
|
||||
struct sockaddr_dl;
|
||||
bool match_ether(const struct sockaddr_dl *sdl);
|
||||
bool match_if_flags(struct ifconfig_args *args, int if_flags);
|
||||
int ifconfig(int argc, char *const *argv, int iscreate, const struct afswtch *uafp);
|
||||
int ifconfig(if_ctx *ctx, int iscreate, const struct afswtch *uafp);
|
||||
bool group_member(const char *ifname, const char *match, const char *nomatch);
|
||||
void print_ifcap(struct ifconfig_args *args, int s);
|
||||
void tunnel_status(int s);
|
||||
@ -273,6 +281,9 @@ void print_metric(int s);
|
||||
|
||||
/* Netlink-related functions */
|
||||
void list_interfaces_nl(struct ifconfig_args *args);
|
||||
int ifconfig_wrapper_nl(struct ifconfig_args *args, int iscreate,
|
||||
const struct afswtch *uafp);
|
||||
uint32_t if_nametoindex_nl(struct snl_state *ss, const char *ifname);
|
||||
|
||||
/*
|
||||
* XXX expose this so modules that neeed to know of any pending
|
||||
|
@ -122,6 +122,26 @@ nl_init_socket(struct snl_state *ss)
|
||||
err(1, "unable to open netlink socket");
|
||||
}
|
||||
|
||||
int
|
||||
ifconfig_wrapper_nl(struct ifconfig_args *args, int iscreate,
|
||||
const struct afswtch *uafp)
|
||||
{
|
||||
struct snl_state ss = {};
|
||||
struct ifconfig_context ctx = {
|
||||
.args = args,
|
||||
.io_s = -1,
|
||||
.io_ss = &ss,
|
||||
};
|
||||
|
||||
nl_init_socket(&ss);
|
||||
|
||||
int error = ifconfig(&ctx, iscreate, uafp);
|
||||
|
||||
snl_free(&ss);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
struct ifa {
|
||||
struct ifa *next;
|
||||
uint32_t idx;
|
||||
@ -185,6 +205,29 @@ prepare_ifmap(struct snl_state *ss)
|
||||
return (ifmap);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
if_nametoindex_nl(struct snl_state *ss, const char *ifname)
|
||||
{
|
||||
struct snl_writer nw = {};
|
||||
struct snl_parsed_link_simple link = {};
|
||||
|
||||
snl_init_writer(ss, &nw);
|
||||
struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK);
|
||||
snl_reserve_msg_object(&nw, struct ifinfomsg);
|
||||
snl_add_msg_attr_string(&nw, IFLA_IFNAME, ifname);
|
||||
|
||||
if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr))
|
||||
return (0);
|
||||
|
||||
hdr = snl_read_reply(ss, hdr->nlmsg_seq);
|
||||
if (hdr->nlmsg_type != NL_RTM_NEWLINK)
|
||||
return (0);
|
||||
if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, &link))
|
||||
return (0);
|
||||
|
||||
return (link.ifi_index);
|
||||
}
|
||||
|
||||
static void
|
||||
prepare_ifaddrs(struct snl_state *ss, struct ifmap *ifmap)
|
||||
{
|
||||
@ -418,7 +461,7 @@ list_interfaces_nl(struct ifconfig_args *args)
|
||||
} else if (args->argc == 0)
|
||||
status_nl(ctx, iface);
|
||||
else
|
||||
ifconfig(args->argc, args->argv, 0, args->afp);
|
||||
ifconfig(ctx, 0, args->afp);
|
||||
}
|
||||
if (args->namesonly)
|
||||
printf("\n");
|
||||
|
Loading…
Reference in New Issue
Block a user