diff options
author | rsc <devnull@localhost> | 2005-12-31 19:33:03 +0000 |
---|---|---|
committer | rsc <devnull@localhost> | 2005-12-31 19:33:03 +0000 |
commit | 9aec88f29cf8145f887f31a4bfc7299f723b72e8 (patch) | |
tree | 9adf1d233e7e947a506539efe2bbcaefd96853f4 /src/libip/Linux.c | |
parent | 9fe7e1a14c84bddc7bb0ec16ce23f44b5479ce94 (diff) | |
download | plan9port-9aec88f29cf8145f887f31a4bfc7299f723b72e8.tar.gz plan9port-9aec88f29cf8145f887f31a4bfc7299f723b72e8.tar.bz2 plan9port-9aec88f29cf8145f887f31a4bfc7299f723b72e8.zip |
new
Diffstat (limited to 'src/libip/Linux.c')
-rw-r--r-- | src/libip/Linux.c | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/src/libip/Linux.c b/src/libip/Linux.c new file mode 100644 index 00000000..1419e718 --- /dev/null +++ b/src/libip/Linux.c @@ -0,0 +1,254 @@ +#include <u.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <asm/types.h> +#include <net/if_arp.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <net/if.h> +#include <libc.h> +#include <ip.h> + +/* + * Use netlink sockets to find interfaces. + * Thanks to Erik Quanstrom. + */ +static int +netlinkrequest(int fd, int type, int (*fn)(struct nlmsghdr *h, Ipifc**, int), + Ipifc **ifc, int index) +{ + char buf[1024]; + int n; + struct sockaddr_nl nl; + struct { + struct nlmsghdr nlh; + struct rtgenmsg g; + } req; + struct nlmsghdr *h; + + memset(&nl, 0, sizeof nl); + nl.nl_family = AF_NETLINK; + + memset(&req, 0, sizeof req); + req.nlh.nlmsg_len = sizeof req; + req.nlh.nlmsg_type = type; + req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; + req.nlh.nlmsg_pid = 0; + req.nlh.nlmsg_seq = 1; + req.g.rtgen_family = AF_NETLINK; + + if(sendto(fd, (void*)&req, sizeof req, 0, (struct sockaddr*)&nl, sizeof nl) < 0) + return -1; + + while((n=read(fd, buf, sizeof buf)) > 0){ + for(h=(struct nlmsghdr*)buf; NLMSG_OK(h, n); h = NLMSG_NEXT(h, n)){ + if(h->nlmsg_type == NLMSG_DONE) + return 0; + if(h->nlmsg_type == NLMSG_ERROR){ + werrstr("netlink error"); + return -1; + } + if(fn(h, ifc, index) < 0) + return -1; + } + } + werrstr("netlink error"); + return -1; +} + +static int +devsocket(void) +{ + /* we couldn't care less which one; just want to talk to the kernel! */ + static int dumb[3] = { AF_INET, AF_PACKET, AF_INET6 }; + int i, fd; + + for(i=0; i<nelem(dumb); i++) + if((fd = socket(dumb[i], SOCK_DGRAM, 0)) >= 0) + return fd; + return -1; +} + +static int +parsertattr(struct rtattr **dst, int ndst, struct nlmsghdr *h, int type, int skip) +{ + struct rtattr *src; + int len; + + len = h->nlmsg_len - NLMSG_LENGTH(skip); + if(len < 0 || h->nlmsg_type != type){ + werrstr("attrs too short"); + return -1; + } + src = (struct rtattr*)((char*)NLMSG_DATA(h) + NLMSG_ALIGN(skip)); + + memset(dst, 0, ndst*sizeof dst[0]); + for(; RTA_OK(src, len); src = RTA_NEXT(src, len)) + if(src->rta_type < ndst) + dst[src->rta_type] = src; + return 0; +} + +static void +rta2ip(int af, uchar *ip, struct rtattr *rta) +{ + memset(ip, 0, IPaddrlen); + + switch(af){ + case AF_INET: + memmove(ip, v4prefix, IPv4off); + memmove(ip+IPv4off, RTA_DATA(rta), IPv4addrlen); + break; + case AF_INET6: + memmove(ip, RTA_DATA(rta), IPaddrlen); + break; + } +} + +static int +getlink(struct nlmsghdr *h, Ipifc **ipifclist, int index) +{ + char *p; + int fd; + struct rtattr *attr[IFLA_MAX+1]; + struct ifinfomsg *ifi; + Ipifc *ifc; + + ifi = (struct ifinfomsg*)NLMSG_DATA(h); + if(index >= 0 && ifi->ifi_index != index) + return 0; + + ifc = mallocz(sizeof *ifc, 1); + if(ifc == nil) + return -1; + ifc->index = ifi->ifi_index; + + while(*ipifclist) + ipifclist = &(*ipifclist)->next; + *ipifclist = ifc; + + if(parsertattr(attr, nelem(attr), h, RTM_NEWLINK, sizeof(struct ifinfomsg)) < 0) + return -1; + + if(attr[IFLA_IFNAME]) + p = (char*)RTA_DATA(attr[IFLA_IFNAME]); + else + p = "nil"; + strecpy(ifc->dev, ifc->dev+sizeof ifc->dev, p); + + if(attr[IFLA_MTU]) + ifc->mtu = *(int*)RTA_DATA(attr[IFLA_MTU]); + + if(attr[IFLA_STATS]){ + struct rtnl_link_stats *s; + + s = RTA_DATA(attr[IFLA_STATS]); + ifc->pktin = s->rx_packets; + ifc->pktout = s->tx_packets; + ifc->errin = s->rx_errors; + ifc->errout = s->tx_errors; + } + + if((fd = devsocket()) > 0){ + struct ifreq ifr; + + memset(&ifr, 0, sizeof ifr); + strncpy(ifr.ifr_name, p, IFNAMSIZ); + ifr.ifr_mtu = 0; + if(ioctl(fd, SIOCGIFMTU, &ifr) >= 0) + ifc->rp.linkmtu = ifr.ifr_mtu; + + memset(&ifr, 0, sizeof ifr); + strncpy(ifr.ifr_name, p, IFNAMSIZ); + if(ioctl(fd, SIOCGIFHWADDR, &ifr) >= 0 + && ifr.ifr_hwaddr.sa_family == ARPHRD_ETHER) + memmove(ifc->ether, ifr.ifr_hwaddr.sa_data, 6); + + close(fd); + } + return 0; +} + +static int +getaddr(struct nlmsghdr *h, Ipifc **ipifclist, int index) +{ + int mask; + Ipifc *ifc; + Iplifc *lifc, **l; + struct ifaddrmsg *ifa; + struct rtattr *attr[IFA_MAX+1]; + + USED(index); + + ifa = (struct ifaddrmsg*)NLMSG_DATA(h); + for(ifc=*ipifclist; ifc; ifc=ifc->next) + if(ifc->index == ifa->ifa_index) + break; + if(ifc == nil) + return 0; + if(parsertattr(attr, nelem(attr), h, RTM_NEWADDR, sizeof(struct ifaddrmsg)) < 0) + return -1; + + lifc = mallocz(sizeof *lifc, 1); + if(lifc == nil) + return -1; + for(l=&ifc->lifc; *l; l=&(*l)->next) + ; + *l = lifc; + + if(attr[IFA_ADDRESS] == nil) + attr[IFA_ADDRESS] = attr[IFA_LOCAL]; + if(attr[IFA_ADDRESS] == nil) + return 0; + + rta2ip(ifa->ifa_family, lifc->ip, attr[IFA_ADDRESS]); + + mask = ifa->ifa_prefixlen/8; + if(ifa->ifa_family == AF_INET) + mask += IPv4off; + memset(lifc->mask, 0xFF, mask); + memmove(lifc->net, lifc->ip, mask); + + if(attr[IFA_CACHEINFO]){ + struct ifa_cacheinfo *ci; + + ci = RTA_DATA(attr[IFA_CACHEINFO]); + lifc->preflt = ci->ifa_prefered; + lifc->validlt = ci->ifa_valid; + } + return 0; +} + +Ipifc* +readipifc(char *net, Ipifc *ifc, int index) +{ + int fd; + + USED(net); + freeipifc(ifc); + ifc = nil; + + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if(fd < 0) + return nil; + ifc = nil; + if(netlinkrequest(fd, RTM_GETLINK, getlink, &ifc, index) < 0 + || netlinkrequest(fd, RTM_GETADDR, getaddr, &ifc, index) < 0){ + close(fd); + freeipifc(ifc); + return nil; + } + close(fd); + return ifc; +} + +int +_myetheraddr(uchar *to, char *dev) +{ + int fd; + struct ifreq ifr; + + return 0; +} |