diff --git a/lib/libifconfig/libifconfig.h b/lib/libifconfig/libifconfig.h --- a/lib/libifconfig/libifconfig.h +++ b/lib/libifconfig/libifconfig.h @@ -382,3 +382,46 @@ * length of *lenp * IFNAMSIZ bytes. */ int ifconfig_list_cloners(ifconfig_handle_t *h, char **bufp, size_t *lenp); + +/** Adds an IPv4 address to an interface + * @param h An open ifconfig state object + * @param ifname The interface name + * @param addr The IPv4 address to be added + * @return 0 on success, nonzero on failure. + * On failure, the error info on the handle is set. + */ +int ifconfig_add_inet(ifconfig_handle_t *h, const char *ifname, + const struct ifconfig_inet_addr *addr); + +/** Delete an IPv4 address on an interface + * @param h An open ifconfig state object + * @param ifname The interface name + * @param addr The IPv4 address to be deleted + * @return 0 on success, nonzero on failure. + * On failure, the error info on the handle is set. + */ +int ifconfig_del_inet(ifconfig_handle_t *h, const char *ifname, + const struct ifconfig_inet_addr *addr); + +/** Adds an IPv6 address to an interface + * @param h An open ifconfig state object + * @param ifname The interface name + * @param addr The IPv6 address to be added. + * For non-P2P addresses, + * either only the dst field is set + * or both sin6 and dst fields are set to the same address. + * @return 0 on success, nonzero on failure. + * On failure, the error info on the handle is set. + */ +int ifconfig_add_inet6(ifconfig_handle_t *h, const char *ifname, + const struct ifconfig_inet6_addr *addr); + +/** Delete an IPv6 address on an interface + * @param h An open ifconfig state object + * @param ifname The interface name + * @param addr The IPv6 address to be deleted + * @return 0 on success, nonzero on failure. + * On failure, the error info on the handle is set. + */ +int ifconfig_del_inet6(ifconfig_handle_t *h, const char *ifname, + const struct ifconfig_inet6_addr *addr); diff --git a/lib/libifconfig/libifconfig_inet.c b/lib/libifconfig/libifconfig_inet.c --- a/lib/libifconfig/libifconfig_inet.c +++ b/lib/libifconfig/libifconfig_inet.c @@ -31,6 +31,10 @@ #include #include +#include +#include +#include +#include #include #include @@ -42,6 +46,9 @@ static const struct sockaddr_in NULL_SIN; +static int ifconfig_inet_exec_nl(ifconfig_handle_t *h, int action, + const char *ifname, const struct ifconfig_inet_addr *addr); + static int inet_prefixlen(const struct in_addr *addr) { @@ -94,3 +101,85 @@ return (0); } + +static int +ifconfig_inet_exec_nl(ifconfig_handle_t *h, int action, const char *ifname, + const struct ifconfig_inet_addr *addr) +{ + int ret = 0, nested_offset = 0; + struct snl_state ss; + struct snl_writer nw = { 0 }; + struct nlmsghdr *hdr; + struct ifaddrmsg *ifahdr; + struct snl_errmsg_data e = { 0 }; + + assert(addr != NULL); + + if (!snl_init(&ss, NETLINK_ROUTE)) { + ifconfig_error(h, NETLINK, ENOTSUP); + return (-1); + } + + snl_init_writer(&ss, &nw); + hdr = snl_create_msg_request(&nw, action); + ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg); + + ifahdr->ifa_family = AF_INET; + ifahdr->ifa_prefixlen = addr->prefixlen; + + ifahdr->ifa_index = if_nametoindex(ifname); + if (ifahdr->ifa_index == 0) { + ifconfig_error(h, OTHER, EADDRNOTAVAIL); + ret = -1; + goto out; + } + + if (addr->sin != NULL) + snl_add_msg_attr_ip4(&nw, IFA_LOCAL, &addr->sin->sin_addr); + if (addr->dst != NULL) + snl_add_msg_attr_ip4(&nw, IFA_ADDRESS, &addr->dst->sin_addr); + if (addr->broadcast != NULL) + snl_add_msg_attr_ip4(&nw, IFA_BROADCAST, + &addr->broadcast->sin_addr); + + nested_offset = snl_add_msg_attr_nested(&nw, IFA_FREEBSD); + if (addr->vhid != 0) + snl_add_msg_attr_u32(&nw, IFAF_VHID, addr->vhid); + snl_end_attr_nested(&nw, nested_offset); + + if ((hdr = snl_finalize_msg(&nw)) == NULL) { + ifconfig_error(h, NETLINK, ENOMEM); + ret = -1; + goto out; + } + + if (!snl_send_message(&ss, hdr)) { + ifconfig_error(h, NETLINK, EIO); + ret = -1; + goto out; + } + + if (!snl_read_reply_code(&ss, hdr->nlmsg_seq, &e)) { + ifconfig_error(h, NETLINK, e.error); + ret = -1; + goto out; + } + +out: + snl_free(&ss); + return (ret); +} + +int +ifconfig_add_inet(ifconfig_handle_t *h, const char *ifname, + const struct ifconfig_inet_addr *addr) +{ + return (ifconfig_inet_exec_nl(h, NL_RTM_NEWADDR, ifname, addr)); +} + +int +ifconfig_del_inet(ifconfig_handle_t *h, const char *ifname, + const struct ifconfig_inet_addr *addr) +{ + return (ifconfig_inet_exec_nl(h, NL_RTM_DELADDR, ifname, addr)); +} diff --git a/lib/libifconfig/libifconfig_inet6.c b/lib/libifconfig/libifconfig_inet6.c --- a/lib/libifconfig/libifconfig_inet6.c +++ b/lib/libifconfig/libifconfig_inet6.c @@ -31,6 +31,10 @@ #include #include +#include +#include +#include +#include #include #include @@ -40,6 +44,8 @@ #include "libifconfig.h" #include "libifconfig_internal.h" +static int ifconfig_inet6_exec_nl(ifconfig_handle_t *h, int action, + const char *ifname, const struct ifconfig_inet6_addr *addr); static int inet6_prefixlen(struct in6_addr *addr) @@ -101,3 +107,89 @@ return (0); } + +static int +ifconfig_inet6_exec_nl(ifconfig_handle_t *h, int action, const char *ifname, + const struct ifconfig_inet6_addr *addr) +{ + int ret = 0, nested_offset = 0; + struct snl_state ss; + struct snl_writer nw = { 0 }; + struct nlmsghdr *hdr; + struct ifaddrmsg *ifahdr; + struct ifa_cacheinfo ci = { 0 }; + struct snl_errmsg_data e = { 0 }; + + assert(addr != NULL); + + if (!snl_init(&ss, NETLINK_ROUTE)) { + ifconfig_error(h, NETLINK, ENOTSUP); + return (-1); + } + + snl_init_writer(&ss, &nw); + hdr = snl_create_msg_request(&nw, action); + ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg); + + ifahdr->ifa_family = AF_INET6; + ifahdr->ifa_prefixlen = addr->prefixlen; + + ifahdr->ifa_index = if_nametoindex(ifname); + if (ifahdr->ifa_index == 0) { + ifconfig_error(h, OTHER, EADDRNOTAVAIL); + ret = -1; + goto out; + } + + if (addr->sin6 != NULL) + snl_add_msg_attr_ip6(&nw, IFA_LOCAL, &addr->sin6->sin6_addr); + if (addr->dstin6 != NULL) + snl_add_msg_attr_ip6(&nw, IFA_ADDRESS, + &addr->dstin6->sin6_addr); + + ci.ifa_prefered = addr->lifetime.ia6t_pltime; + ci.ifa_valid = addr->lifetime.ia6t_vltime; + snl_add_msg_attr(&nw, IFA_CACHEINFO, sizeof(ci), &ci); + + nested_offset = snl_add_msg_attr_nested(&nw, IFA_FREEBSD); + snl_add_msg_attr_u32(&nw, IFAF_FLAGS, addr->flags); + if (addr->vhid != 0) + snl_add_msg_attr_u32(&nw, IFAF_VHID, addr->vhid); + snl_end_attr_nested(&nw, nested_offset); + + if ((hdr = snl_finalize_msg(&nw)) == NULL) { + ifconfig_error(h, NETLINK, ENOMEM); + ret = -1; + goto out; + } + + if (!snl_send_message(&ss, hdr)) { + ifconfig_error(h, NETLINK, EIO); + ret = -1; + goto out; + } + + if (!snl_read_reply_code(&ss, hdr->nlmsg_seq, &e)) { + ifconfig_error(h, NETLINK, e.error); + ret = -1; + goto out; + } + +out: + snl_free(&ss); + return (ret); +} + +int +ifconfig_add_inet6(ifconfig_handle_t *h, const char *ifname, + const struct ifconfig_inet6_addr *addr) +{ + return (ifconfig_inet6_exec_nl(h, NL_RTM_NEWADDR, ifname, addr)); +} + +int +ifconfig_del_inet6(ifconfig_handle_t *h, const char *ifname, + const struct ifconfig_inet6_addr *addr) +{ + return (ifconfig_inet6_exec_nl(h, NL_RTM_DELADDR, ifname, addr)); +}