Blob


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>
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;
31 memset(&nl, 0, sizeof nl);
32 nl.nl_family = AF_NETLINK;
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;
42 if(sendto(fd, (void*)&req, sizeof req, 0, (struct sockaddr*)&nl, sizeof nl) < 0)
43 return -1;
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 }
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;
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 }
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;
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));
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 }
94 static void
95 rta2ip(int af, uchar *ip, struct rtattr *rta)
96 {
97 memset(ip, 0, IPaddrlen);
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;
110 static int
111 getlink(struct nlmsghdr *h, Ipifc **ipifclist, int index)
113 char *p;
114 int fd;
115 struct rtattr *attr[IFLA_MAX+1];
116 struct ifinfomsg *ifi;
117 Ipifc *ifc;
119 ifi = (struct ifinfomsg*)NLMSG_DATA(h);
120 if(index >= 0 && ifi->ifi_index != index)
121 return 0;
123 ifc = mallocz(sizeof *ifc, 1);
124 if(ifc == nil)
125 return -1;
126 ifc->index = ifi->ifi_index;
128 while(*ipifclist)
129 ipifclist = &(*ipifclist)->next;
130 *ipifclist = ifc;
132 if(parsertattr(attr, nelem(attr), h, RTM_NEWLINK, sizeof(struct ifinfomsg)) < 0)
133 return -1;
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);
141 if(attr[IFLA_MTU])
142 ifc->mtu = *(int*)RTA_DATA(attr[IFLA_MTU]);
144 /*
145 * Does not work on old Linux systems,
146 * and not really necessary for my purposes.
147 * Uncomment if you want it bad.
149 if(attr[IFLA_STATS]){
150 struct rtnl_link_stats *s;
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;
159 */
161 if((fd = devsocket()) > 0){
162 struct ifreq ifr;
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;
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);
176 close(fd);
178 return 0;
181 static int
182 getaddr(struct nlmsghdr *h, Ipifc **ipifclist, int index)
184 int mask;
185 Ipifc *ifc;
186 Iplifc *lifc, **l;
187 struct ifaddrmsg *ifa;
188 struct rtattr *attr[IFA_MAX+1];
190 USED(index);
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;
201 lifc = mallocz(sizeof *lifc, 1);
202 if(lifc == nil)
203 return -1;
204 for(l=&ifc->lifc; *l; l=&(*l)->next)
206 *l = lifc;
208 if(attr[IFA_ADDRESS] == nil)
209 attr[IFA_ADDRESS] = attr[IFA_LOCAL];
210 if(attr[IFA_ADDRESS] == nil)
211 return 0;
213 rta2ip(ifa->ifa_family, lifc->ip, attr[IFA_ADDRESS]);
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);
221 if(attr[IFA_CACHEINFO]){
222 struct ifa_cacheinfo *ci;
224 ci = RTA_DATA(attr[IFA_CACHEINFO]);
225 lifc->preflt = ci->ifa_prefered;
226 lifc->validlt = ci->ifa_valid;
228 return 0;
231 Ipifc*
232 readipifc(char *net, Ipifc *ifc, int index)
234 int fd;
236 USED(net);
237 freeipifc(ifc);
238 ifc = nil;
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;
250 close(fd);
251 return ifc;