plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

Linux.c (5339B)


      1 #include <u.h>
      2 #include <sys/ioctl.h>
      3 #include <sys/socket.h>
      4 #include <netinet/in.h>
      5 #include <arpa/inet.h>
      6 #include <asm/types.h>
      7 #include <net/if_arp.h>
      8 #include <linux/netlink.h>
      9 #include <linux/rtnetlink.h>
     10 #include <net/if.h>
     11 #include <libc.h>
     12 #include <ip.h>
     13 
     14 /*
     15  * Use netlink sockets to find interfaces.
     16  * Thanks to Erik Quanstrom.
     17  */
     18 static int
     19 netlinkrequest(int fd, int type, int (*fn)(struct nlmsghdr *h, Ipifc**, int),
     20 	Ipifc **ifc, int index)
     21 {
     22 	char buf[1024];
     23 	int n;
     24 	struct sockaddr_nl nl;
     25 	struct {
     26 		struct nlmsghdr nlh;
     27 		struct rtgenmsg g;
     28 	} req;
     29 	struct nlmsghdr *h;
     30 
     31 	memset(&nl, 0, sizeof nl);
     32 	nl.nl_family = AF_NETLINK;
     33 
     34 	memset(&req, 0, sizeof req);
     35 	req.nlh.nlmsg_len = sizeof req;
     36 	req.nlh.nlmsg_type = type;
     37 	req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
     38 	req.nlh.nlmsg_pid = 0;
     39 	req.nlh.nlmsg_seq = 1;
     40 	req.g.rtgen_family = AF_NETLINK;
     41 
     42 	if(sendto(fd, (void*)&req, sizeof req, 0, (struct sockaddr*)&nl, sizeof nl) < 0)
     43 		return -1;
     44 
     45 	while((n=read(fd, buf, sizeof buf)) > 0){
     46 		for(h=(struct nlmsghdr*)buf; NLMSG_OK(h, n); h = NLMSG_NEXT(h, n)){
     47 			if(h->nlmsg_type == NLMSG_DONE)
     48 				return 0;
     49 			if(h->nlmsg_type == NLMSG_ERROR){
     50 				werrstr("netlink error");
     51 				return -1;
     52 			}
     53 			if(fn(h, ifc, index) < 0)
     54 				return -1;
     55 		}
     56 	}
     57 	werrstr("netlink error");
     58 	return -1;
     59 }
     60 
     61 static int
     62 devsocket(void)
     63 {
     64 	/* we couldn't care less which one; just want to talk to the kernel! */
     65 	static int dumb[3] = { AF_INET, AF_PACKET, AF_INET6 };
     66 	int i, fd;
     67 
     68 	for(i=0; i<nelem(dumb); i++)
     69 		if((fd = socket(dumb[i], SOCK_DGRAM, 0)) >= 0)
     70 			return fd;
     71 	return -1;
     72 }
     73 
     74 static int
     75 parsertattr(struct rtattr **dst, int ndst, struct nlmsghdr *h, int type, int skip)
     76 {
     77 	struct rtattr *src;
     78 	int len;
     79 
     80 	len = h->nlmsg_len - NLMSG_LENGTH(skip);
     81 	if(len < 0 || h->nlmsg_type != type){
     82 		werrstr("attrs too short");
     83 		return -1;
     84 	}
     85 	src = (struct rtattr*)((char*)NLMSG_DATA(h) + NLMSG_ALIGN(skip));
     86 
     87 	memset(dst, 0, ndst*sizeof dst[0]);
     88 	for(; RTA_OK(src, len); src = RTA_NEXT(src, len))
     89 		if(src->rta_type < ndst)
     90 			dst[src->rta_type] = src;
     91 	return 0;
     92 }
     93 
     94 static void
     95 rta2ip(int af, uchar *ip, struct rtattr *rta)
     96 {
     97 	memset(ip, 0, IPaddrlen);
     98 
     99 	switch(af){
    100 	case AF_INET:
    101 		memmove(ip, v4prefix, IPv4off);
    102 		memmove(ip+IPv4off, RTA_DATA(rta), IPv4addrlen);
    103 		break;
    104 	case AF_INET6:
    105 		memmove(ip, RTA_DATA(rta), IPaddrlen);
    106 		break;
    107 	}
    108 }
    109 
    110 static int
    111 getlink(struct nlmsghdr *h, Ipifc **ipifclist, int index)
    112 {
    113 	char *p;
    114 	int fd;
    115 	struct rtattr *attr[IFLA_MAX+1];
    116 	struct ifinfomsg *ifi;
    117 	Ipifc *ifc;
    118 
    119 	ifi = (struct ifinfomsg*)NLMSG_DATA(h);
    120 	if(index >= 0 && ifi->ifi_index != index)
    121 		return 0;
    122 
    123 	ifc = mallocz(sizeof *ifc, 1);
    124 	if(ifc == nil)
    125 		return -1;
    126 	ifc->index = ifi->ifi_index;
    127 
    128 	while(*ipifclist)
    129 		ipifclist = &(*ipifclist)->next;
    130 	*ipifclist = ifc;
    131 
    132 	if(parsertattr(attr, nelem(attr), h, RTM_NEWLINK, sizeof(struct ifinfomsg)) < 0)
    133 		return -1;
    134 
    135 	if(attr[IFLA_IFNAME])
    136 		p = (char*)RTA_DATA(attr[IFLA_IFNAME]);
    137 	else
    138 		p = "nil";
    139 	strecpy(ifc->dev, ifc->dev+sizeof ifc->dev, p);
    140 
    141 	if(attr[IFLA_MTU])
    142 		ifc->mtu = *(int*)RTA_DATA(attr[IFLA_MTU]);
    143 
    144 	/*
    145 	 * Does not work on old Linux systems,
    146 	 * and not really necessary for my purposes.
    147 	 * Uncomment if you want it bad.
    148 	 *
    149 	if(attr[IFLA_STATS]){
    150 		struct rtnl_link_stats *s;
    151 
    152 		s = RTA_DATA(attr[IFLA_STATS]);
    153 		ifc->pktin = s->rx_packets;
    154 		ifc->pktout = s->tx_packets;
    155 		ifc->errin = s->rx_errors;
    156 		ifc->errout = s->tx_errors;
    157 	}
    158 	 *
    159 	 */
    160 
    161 	if((fd = devsocket()) > 0){
    162 		struct ifreq ifr;
    163 
    164 		memset(&ifr, 0, sizeof ifr);
    165 		strncpy(ifr.ifr_name, p, IFNAMSIZ);
    166 		ifr.ifr_mtu = 0;
    167 		if(ioctl(fd, SIOCGIFMTU, &ifr) >= 0)
    168 			ifc->rp.linkmtu = ifr.ifr_mtu;
    169 
    170 		memset(&ifr, 0, sizeof ifr);
    171 		strncpy(ifr.ifr_name, p, IFNAMSIZ);
    172 		if(ioctl(fd, SIOCGIFHWADDR, &ifr) >= 0
    173 		&& ifr.ifr_hwaddr.sa_family == ARPHRD_ETHER)
    174 			memmove(ifc->ether, ifr.ifr_hwaddr.sa_data, 6);
    175 
    176 		close(fd);
    177 	}
    178 	return 0;
    179 }
    180 
    181 static int
    182 getaddr(struct nlmsghdr *h, Ipifc **ipifclist, int index)
    183 {
    184 	int mask;
    185 	Ipifc *ifc;
    186 	Iplifc *lifc, **l;
    187 	struct ifaddrmsg *ifa;
    188 	struct rtattr *attr[IFA_MAX+1];
    189 
    190 	USED(index);
    191 
    192 	ifa = (struct ifaddrmsg*)NLMSG_DATA(h);
    193 	for(ifc=*ipifclist; ifc; ifc=ifc->next)
    194 		if(ifc->index == ifa->ifa_index)
    195 			break;
    196 	if(ifc == nil)
    197 		return 0;
    198 	if(parsertattr(attr, nelem(attr), h, RTM_NEWADDR, sizeof(struct ifaddrmsg)) < 0)
    199 		return -1;
    200 
    201 	lifc = mallocz(sizeof *lifc, 1);
    202 	if(lifc == nil)
    203 		return -1;
    204 	for(l=&ifc->lifc; *l; l=&(*l)->next)
    205 		;
    206 	*l = lifc;
    207 
    208 	if(attr[IFA_ADDRESS] == nil)
    209 		attr[IFA_ADDRESS] = attr[IFA_LOCAL];
    210 	if(attr[IFA_ADDRESS] == nil)
    211 		return 0;
    212 
    213 	rta2ip(ifa->ifa_family, lifc->ip, attr[IFA_ADDRESS]);
    214 
    215 	mask = ifa->ifa_prefixlen/8;
    216 	if(ifa->ifa_family == AF_INET)
    217 		mask += IPv4off;
    218 	memset(lifc->mask, 0xFF, mask);
    219 	memmove(lifc->net, lifc->ip, mask);
    220 
    221 	if(attr[IFA_CACHEINFO]){
    222 		struct ifa_cacheinfo *ci;
    223 
    224 		ci = RTA_DATA(attr[IFA_CACHEINFO]);
    225 		lifc->preflt = ci->ifa_prefered;
    226 		lifc->validlt = ci->ifa_valid;
    227 	}
    228 	return 0;
    229 }
    230 
    231 Ipifc*
    232 readipifc(char *net, Ipifc *ifc, int index)
    233 {
    234 	int fd;
    235 
    236 	USED(net);
    237 	freeipifc(ifc);
    238 	ifc = nil;
    239 
    240 	fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
    241 	if(fd < 0)
    242 		return nil;
    243 	ifc = nil;
    244 	if(netlinkrequest(fd, RTM_GETLINK, getlink, &ifc, index) < 0
    245 	|| netlinkrequest(fd, RTM_GETADDR, getaddr, &ifc, index) < 0){
    246 		close(fd);
    247 		freeipifc(ifc);
    248 		return nil;
    249 	}
    250 	close(fd);
    251 	return ifc;
    252 }