X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=src%2Fvip6-autod.c;h=1b60aa0d79fa0611b2ccfc74c3d1a6dd0657a26e;hb=5f167675beabb9f55df5fe8a579f87763764cb08;hp=f45dce5f80b77e7eaf000372154cc6cdc8e376e4;hpb=67fe4e14443e2735adbacc6878053348ab5e40a4;p=util-vserver.git diff --git a/src/vip6-autod.c b/src/vip6-autod.c index f45dce5..1b60aa0 100644 --- a/src/vip6-autod.c +++ b/src/vip6-autod.c @@ -1,3 +1,16 @@ +/* + * $Id: vip6-autod.c,v 1.4 2007/07/30 14:59:11 dhozac Exp $ + * Copyright (c) 2007 The Trustees of Princeton University + * Author: Daniel Hokka Zakrisson + * + * Licensed under the terms of the GNU General Public License + * version 2 or later. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + #include #include #include @@ -11,23 +24,25 @@ #include #include #include +#include +#include #include -#include -#include -/*XXX #include */ -struct in6_ifreq { - struct in6_addr ifr6_addr; - __u32 ifr6_prefixlen; - int ifr6_ifindex; -}; +/* not defined for gcc -ansi */ +typedef uint64_t __u64; +typedef int64_t __s64; +#include +#include -typedef unsigned int nid_t; -typedef unsigned int xid_t; #include +#include "pathconfig.h" #define HAS_ADDRESS 0x01 #define HAS_PREFIX 0x02 +struct nid_list { + nid_t nid; + struct nid_list *next; +}; struct prefix_list { struct prefix_list *prev; struct prefix_list *next; @@ -43,8 +58,11 @@ struct prefix_list { int prefix_len; time_t valid_until; } address; + struct nid_list *nids; }; +struct nl_handle *handle; + /* from linux/include/net/ipv6.h */ static inline int ipv6_prefix_equal(struct in6_addr *prefix, struct in6_addr *addr, int prefixlen) @@ -67,25 +85,72 @@ static inline int ipv6_prefix_equal(struct in6_addr *prefix, static int add_address_to_interface(int ifindex, struct in6_addr *address, int prefix) { - struct in6_ifreq ireq; - int sock; + int err = -1; + struct rtnl_addr *rta; + struct nl_addr *nl; - ireq.ifr6_ifindex = ifindex; - ireq.ifr6_prefixlen = prefix; - memcpy(&ireq.ifr6_addr, address, sizeof(*address)); + nl = nl_addr_build(AF_INET6, address, sizeof(struct in6_addr)); + rta = rtnl_addr_alloc(); - /* XXX should use netlink */ - sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP); - if (sock == -1) - return -1; + rtnl_addr_set_family(rta, AF_INET6); + rtnl_addr_set_ifindex(rta, ifindex); + rtnl_addr_set_local(rta, nl); + rtnl_addr_set_prefixlen(rta, prefix); - if (ioctl(sock, SIOCSIFADDR, &ireq) == -1 && errno != EEXIST) { - close(sock); - return -1; + if (rtnl_addr_add(handle, rta, NLM_F_REPLACE) != -1 || errno == EEXIST) + err = 0; + + rtnl_addr_free(rta); + nl_addr_destroy(nl); + return err; +} + +static int add_nid_to_list(struct nid_list **l, nid_t nid) +{ + struct nid_list *n; + for (n = *l; n; n = n->next) { + if (n->nid == nid) + return 0; } + n = calloc(1, sizeof(struct nid_list)); + if (!n) + return -1; + n->nid = nid; + n->next = *l; + *l = n; + return 1; +} - close(sock); - return 0; +static void cleanup_prefix(struct prefix_list *i) +{ + struct nid_list *n, *p = NULL; + + for (n = i->nids; n; n = n->next) { + struct rtnl_addr *rta; + struct nl_addr *nl; + struct in6_addr a; + + if (p) + free(p); + memcpy(&a, &i->address.addr, sizeof(a)); + rta = rtnl_addr_alloc(); + nl = nl_addr_build(AF_INET6, &a, sizeof(a)); + + rtnl_addr_set_family(rta, AF_INET6); + rtnl_addr_set_ifindex(rta, i->ifindex); + rtnl_addr_set_local(rta, nl); + rtnl_addr_set_prefixlen(rta, i->address.prefix_len); + + /* ignore errors */ + rtnl_addr_delete(handle, rta, 0); + + nl_addr_destroy(nl); + rtnl_addr_free(rta); + + p = n; + } + if (p) + free(p); } static void do_slices_autoconf(struct prefix_list *head) @@ -94,7 +159,8 @@ static void do_slices_autoconf(struct prefix_list *head) struct dirent *de; nid_t nid; struct vc_net_nx addr; - struct prefix_list *i, *expired = NULL; + struct prefix_list *i; + struct nid_list *current = NULL, *n; if ((dp = opendir("/proc/virtnet")) == NULL) return; @@ -103,21 +169,32 @@ static void do_slices_autoconf(struct prefix_list *head) continue; nid = strtoul(de->d_name, NULL, 10); - addr.type = vcNET_IPV6; - addr.count = -1; - if (vc_net_remove(nid, &addr) == -1) + addr.type = vcNET_IPV6A; + addr.count = 0; + if (vc_net_remove(nid, &addr) == -1) { + syslog(LOG_ERR, "vc_net_remove(%u): %s", nid, strerror(errno)); continue; + } + + add_nid_to_list(¤t, nid); for (i = head->next; i;) { /* expired */ if (i->mask & HAS_PREFIX && i->prefix.valid_until < time(NULL)) { struct prefix_list *tmp; + char buf[64]; + + inet_ntop(AF_INET6, &i->address.addr, buf, sizeof(buf)); + syslog(LOG_NOTICE, "Address %s timed out", buf); + if (i->next) i->next->prev = i->prev; if (i->prev) i->prev->next = i->next; - tmp = i->next; + + cleanup_prefix(i); + free(i); i = tmp; continue; @@ -125,18 +202,23 @@ static void do_slices_autoconf(struct prefix_list *head) if (i->mask != (HAS_ADDRESS|HAS_PREFIX)) goto next; + addr.type = vcNET_IPV6; addr.count = 1; addr.mask[0] = i->prefix.prefix_len; memcpy(addr.ip, &i->address.addr, sizeof(struct in6_addr)); - addr.ip[2] = htonl((ntohl(addr.ip[2]) & 0xffffff00) | ((nid & 0x7f00) >> 7)); - addr.ip[3] = htonl((ntohl(addr.ip[3]) & 0x00ffffff) | ((nid & 0xff) << 25)); + addr.ip[2] = htonl((ntohl(addr.ip[2]) & 0xffffff00) | ((nid & 0x7f80) >> 7)); + addr.ip[3] = htonl((ntohl(addr.ip[3]) & 0x00ffffff) | ((nid & 0x7f) << 25)); if (vc_net_add(nid, &addr) == -1) { - perror("vc_net_add"); - exit(1); + syslog(LOG_ERR, "vc_net_add(%u): %s", nid, strerror(errno)); + goto next; } if (add_address_to_interface(i->ifindex, (struct in6_addr *) addr.ip, i->prefix.prefix_len) == -1) { - perror("add_address_to_interface"); - exit(1); + syslog(LOG_ERR, "add_address_to_interface: %s", strerror(errno)); + goto next; + } + if (add_nid_to_list(&i->nids, nid) == -1) { + syslog(LOG_ERR, "add_nid_to_list: %s", strerror(errno)); + goto next; } next: i = i->next; @@ -151,6 +233,9 @@ static int add_prefix(struct prefix_list *head, struct prefixmsg *msg, struct prefix_list *i = head; if (!msg || !prefix || !cache) return -1; + /* XXX IF_PREFIX_AUTOCONF == 0x02 */ + if (!(msg->prefix_flags & 0x02)) + return -1; do { if (i->next != NULL) @@ -187,7 +272,7 @@ static inline int add_address(struct prefix_list *head, struct ifaddrmsg *msg, return -1; if (address->s6_addr[11] != 0xFF || address->s6_addr[12] != 0xFE) - return 0; + return -1; do { if (i->next != NULL) @@ -215,83 +300,131 @@ static inline int add_address(struct prefix_list *head, struct ifaddrmsg *msg, return 1; } -int main(int argc, char *argv[]) +static struct nla_policy addr_policy[IFA_MAX+1] = { + [IFA_ADDRESS] = { .minlen = sizeof(struct in6_addr) }, + [IFA_LABEL] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ }, + [IFA_CACHEINFO] = { .minlen = sizeof(struct ifa_cacheinfo) }, +}; +static struct nla_policy prefix_policy[PREFIX_MAX+1] = { + [PREFIX_ADDRESS] = { .minlen = sizeof(struct in6_addr) }, + [PREFIX_CACHEINFO] = { .minlen = sizeof(struct prefix_cacheinfo) }, +}; +int handle_valid_msg(struct nl_msg *msg, void *arg) { - int sock; - struct sockaddr_nl sa; - struct prefix_list head = { .prev = NULL, .next = NULL }; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + int ret = -1; + char *payload; + struct sockaddr_nl *source = nlmsg_get_src(msg); + + payload = nlmsg_data(nlh); + if (source->nl_groups == RTMGRP_IPV6_PREFIX) { + struct prefixmsg *prefixmsg; + struct in6_addr *prefix = NULL; + struct prefix_cacheinfo *cacheinfo = NULL; + struct nlattr *tb[PREFIX_MAX+1]; + + if (nlmsg_parse(nlh, sizeof(struct prefixmsg), tb, PREFIX_MAX, prefix_policy) < 0) { + syslog(LOG_ERR, "Failed to parse prefixmsg"); + return -1; + } - sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); - if (sock == -1) { - perror("socket()"); - exit(1); - } - sa.nl_family = AF_NETLINK; - sa.nl_groups = RTMGRP_IPV6_PREFIX|RTMGRP_IPV6_IFADDR; - if (bind(sock, (struct sockaddr *) &sa, sizeof(sa)) == -1) { - perror("bind()"); - exit(1); + prefixmsg = (struct prefixmsg *) payload; + if (tb[PREFIX_ADDRESS]) + prefix = nl_data_get(nla_get_data(tb[PREFIX_ADDRESS])); + if (tb[PREFIX_CACHEINFO]) + cacheinfo = nl_data_get(nla_get_data(tb[PREFIX_CACHEINFO])); + ret = add_prefix(arg, prefixmsg, prefix, cacheinfo); + } + else if (source->nl_groups == RTMGRP_IPV6_IFADDR) { + struct ifaddrmsg *ifaddrmsg; + struct in6_addr *address = NULL; + struct ifa_cacheinfo *cacheinfo = NULL; + struct nlattr *tb[IFA_MAX+1]; + + if (nlmsg_parse(nlh, sizeof(struct ifaddrmsg), tb, IFA_MAX, addr_policy) < 0) { + syslog(LOG_ERR, "Failed to parse ifaddrmsg"); + return -1; + } + + ifaddrmsg = (struct ifaddrmsg *) payload; + if (tb[IFA_ADDRESS]) + address = nl_data_get(nla_get_data(tb[IFA_ADDRESS])); + if (tb[IFA_CACHEINFO]) + cacheinfo = nl_data_get(nla_get_data(tb[IFA_CACHEINFO])); + ret = add_address(arg, ifaddrmsg, address, cacheinfo); } + if (ret >= 0) + do_slices_autoconf(arg); - while (1) { - char buf[4000], *payload; - struct nlmsghdr *nlh; - ssize_t len, this_len; - socklen_t socklen = sizeof(sa); - - if ((len = recvfrom(sock, buf, sizeof(buf), 0, - (struct sockaddr *) &sa, &socklen)) <= 0) - break; - for (nlh = (struct nlmsghdr *) buf; NLMSG_OK(nlh, len); nlh = NLMSG_NEXT(nlh, len)) { - struct nlattr *nla; - - if (nlh->nlmsg_type == NLMSG_DONE) - break; - else if (nlh->nlmsg_type == NLMSG_ERROR) - break; - - this_len = NLMSG_ALIGN((nlh)->nlmsg_len) - NLMSG_LENGTH(0); - payload = NLMSG_DATA(nlh); - if (sa.nl_groups == RTMGRP_IPV6_PREFIX) { - struct prefixmsg *prefixmsg; - struct in6_addr *prefix; - struct prefix_cacheinfo *cacheinfo; - - prefixmsg = (struct prefixmsg *) payload; - prefix = NULL; - cacheinfo = NULL; - for (nla = (struct nlattr *)(payload + sizeof(*prefixmsg)); nla < payload + this_len; nla = (char *) nla + nla->nla_len) { - if (nla->nla_type == PREFIX_ADDRESS) - prefix = (struct in6_addr *)(((char *) nla) + sizeof(*nla)); - else if (nla->nla_type == PREFIX_CACHEINFO) - cacheinfo = (struct prefix_cacheinfo *)(((char *) nla) + sizeof(*nla)); - } - if (add_prefix(&head, prefixmsg, prefix, cacheinfo) == -1) { - printf("Adding prefix failed!\n"); - } - } - else if (sa.nl_groups == RTMGRP_IPV6_IFADDR) { - struct ifaddrmsg *ifaddrmsg; - struct in6_addr *address; - struct ifa_cacheinfo *cacheinfo; - - ifaddrmsg = (struct ifaddrmsg *) payload; - address = NULL; - cacheinfo = NULL; - for (nla = (struct nlattr *)(payload + sizeof(*ifaddrmsg)); nla < payload + this_len; nla = (char *) nla + nla->nla_len) { - if (nla->nla_type == IFA_ADDRESS) - address = (struct in6_addr *)(((char *) nla) + sizeof(*nla)); - else if (nla->nla_type == IFA_CACHEINFO) - cacheinfo = (struct ifa_cacheinfo *)(((char *) nla) + sizeof(*nla)); - } - if (add_address(&head, ifaddrmsg, address, cacheinfo) == -1) { - printf("Adding address failed!\n"); - } - } - } + return 0; +} + +int handle_error_msg(struct sockaddr_nl *source, struct nlmsgerr *err, + void *arg) +{ + syslog(LOG_ERR, "%s", strerror(err->error)); + return 0; +} + +int handle_no_op(struct nl_msg *msg, void *arg) +{ + return 0; +} + +/* only for access in the signal handler */ +struct prefix_list head; +void signal_handler(int signal) +{ + switch (signal) { + case SIGUSR1: do_slices_autoconf(&head); + break; + } +} + +static int write_pidfile(const char *filename) +{ + FILE *fp; + fp = fopen(filename, "w"); + if (!fp) + return -1; + fprintf(fp, "%d\n", getpid()); + fclose(fp); + return 0; +} + +int main(int argc, char *argv[]) +{ + struct nl_cb *cbs; + head.prev = head.next = NULL; + + openlog("vip6-autod", LOG_PERROR, LOG_DAEMON); + + handle = nl_handle_alloc_nondefault(NL_CB_VERBOSE); + cbs = nl_handle_get_cb(handle); + nl_cb_set(cbs, NL_CB_VALID, NL_CB_CUSTOM, handle_valid_msg, &head); + nl_cb_set(cbs, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, handle_no_op, NULL); + nl_cb_err(cbs, NL_CB_CUSTOM, handle_error_msg, &head); + nl_disable_sequence_check(handle); + + nl_join_groups(handle, RTMGRP_IPV6_PREFIX|RTMGRP_IPV6_IFADDR); + if (nl_connect(handle, NETLINK_ROUTE) == -1) { + syslog(LOG_CRIT, "nl_connect: %s", strerror(errno)); + exit(1); } - close(sock); + if (daemon(0, 0) == -1) + return -1; + + /* XXX .. here is a hack */ + write_pidfile(DEFAULT_PKGSTATEDIR "/../vip6-autod.pid"); + + signal(SIGUSR1, signal_handler); + + while (nl_recvmsgs(handle, cbs) > 0); + + nl_close(handle); + closelog(); return 0; }