]> git.ipfire.org Git - people/ms/dnsmasq.git/blame - src/netlink.c
import of dnsmasq-2.28.tar.gz
[people/ms/dnsmasq.git] / src / netlink.c
CommitLineData
5e9e0efb 1 /* dnsmasq is Copyright (c) 2000-2006 Simon Kelley
0a852541
SK
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 dated June, 1991.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11*/
12
0a852541
SK
13#include "dnsmasq.h"
14
5e9e0efb 15#ifdef HAVE_LINUX_NETWORK
0a852541 16
91dccd09 17#include <linux/types.h>
0a852541
SK
18#include <linux/netlink.h>
19#include <linux/rtnetlink.h>
20
5e9e0efb
SK
21static struct iovec iov;
22
23static void nl_err(struct nlmsghdr *h);
24static void nl_routechange(struct daemon *daemon, struct nlmsghdr *h);
cdeda28f
SK
25
26void netlink_init(struct daemon *daemon)
0a852541
SK
27{
28 struct sockaddr_nl addr;
0a852541
SK
29
30 addr.nl_family = AF_NETLINK;
31 addr.nl_pad = 0;
5e9e0efb 32 addr.nl_pid = 0; /* autobind */
cdeda28f 33#ifdef HAVE_IPV6
5e9e0efb 34 addr.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE;
cdeda28f 35#else
5e9e0efb 36 addr.nl_groups = RTMGRP_IPV4_ROUTE;
cdeda28f 37#endif
0a852541 38
5e9e0efb
SK
39 if ((daemon->netlinkfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1 ||
40 bind(daemon->netlinkfd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
41 die(_("cannot create RTnetlink socket: %s"), NULL);
42
43 iov.iov_len = 200;
44 iov.iov_base = safe_malloc(iov.iov_len);
0a852541
SK
45}
46
cdeda28f
SK
47static ssize_t netlink_recv(struct daemon *daemon)
48{
49 struct msghdr msg;
50 ssize_t rc;
51
52 msg.msg_control = NULL;
53 msg.msg_controllen = 0;
cdeda28f
SK
54 msg.msg_name = NULL;
55 msg.msg_namelen = 0;
5e9e0efb 56 msg.msg_iov = &iov;
cdeda28f
SK
57 msg.msg_iovlen = 1;
58
5e9e0efb 59 while (1)
cdeda28f 60 {
5e9e0efb
SK
61 msg.msg_flags = 0;
62 while ((rc = recvmsg(daemon->netlinkfd, &msg, MSG_PEEK)) == -1 && errno == EINTR);
63
64 /* 2.2.x doesn't suport MSG_PEEK at all, returning EOPNOTSUPP, so we just grab a
65 big buffer and pray in that case. */
66 if (rc == -1 && errno == EOPNOTSUPP)
67 {
68 if (!expand_buf(&iov, 2000))
69 return -1;
70 break;
71 }
72
73 if (rc == -1 || !(msg.msg_flags & MSG_TRUNC))
74 break;
75
76 if (!expand_buf(&iov, iov.iov_len + 100))
cdeda28f 77 return -1;
cdeda28f 78 }
5e9e0efb
SK
79
80 /* finally, read it for real */
81 while ((rc = recvmsg(daemon->netlinkfd, &msg, 0)) == -1 && errno == EINTR);
cdeda28f
SK
82
83 return rc;
84}
85
5e9e0efb 86int iface_enumerate(struct daemon *daemon, void *parm, int (*ipv4_callback)(), int (*ipv6_callback)())
0a852541
SK
87{
88 struct sockaddr_nl addr;
89 struct nlmsghdr *h;
cdeda28f 90 ssize_t len;
0a852541 91 static unsigned int seq = 0;
5e9e0efb 92 int family = AF_INET;
0a852541
SK
93
94 struct {
95 struct nlmsghdr nlh;
96 struct rtgenmsg g;
97 } req;
98
0a852541
SK
99 addr.nl_family = AF_NETLINK;
100 addr.nl_pad = 0;
101 addr.nl_groups = 0;
102 addr.nl_pid = 0; /* address to kernel */
103
5e9e0efb 104 again:
0a852541
SK
105 req.nlh.nlmsg_len = sizeof(req);
106 req.nlh.nlmsg_type = RTM_GETADDR;
107 req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
108 req.nlh.nlmsg_pid = 0;
109 req.nlh.nlmsg_seq = ++seq;
5e9e0efb 110 req.g.rtgen_family = family;
0a852541
SK
111
112 /* Don't block in recvfrom if send fails */
113 while((len = sendto(daemon->netlinkfd, (void *)&req, sizeof(req), 0,
114 (struct sockaddr *)&addr, sizeof(addr))) == -1 && retry_send());
5e9e0efb 115
0a852541 116 if (len == -1)
5e9e0efb
SK
117 return 0;
118
119 while (1)
0a852541 120 {
5e9e0efb
SK
121 if ((len = netlink_recv(daemon)) == -1)
122 return 0;
123
124 for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
125 if (h->nlmsg_type == NLMSG_ERROR)
126 nl_err(h);
127 else if (h->nlmsg_seq != seq)
128 nl_routechange(daemon, h); /* May be multicast arriving async */
129 else if (h->nlmsg_type == NLMSG_DONE)
130 {
131#ifdef HAVE_IPV6
132 if (family == AF_INET && ipv6_callback)
133 {
134 family = AF_INET6;
135 goto again;
136 }
137#endif
138 return 1;
139 }
140 else if (h->nlmsg_type == RTM_NEWADDR)
141 {
142 struct ifaddrmsg *ifa = NLMSG_DATA(h);
143 struct rtattr *rta = IFA_RTA(ifa);
144 unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa));
145
146 if (ifa->ifa_family == AF_INET)
147 {
148 struct in_addr netmask, addr, broadcast;
149
150 netmask.s_addr = htonl(0xffffffff << (32 - ifa->ifa_prefixlen));
151 addr.s_addr = 0;
152 broadcast.s_addr = 0;
153
154 while (RTA_OK(rta, len1))
155 {
156 if (rta->rta_type == IFA_LOCAL)
157 addr = *((struct in_addr *)(rta+1));
158 else if (rta->rta_type == IFA_BROADCAST)
159 broadcast = *((struct in_addr *)(rta+1));
160
161 rta = RTA_NEXT(rta, len1);
162 }
163
164 if (addr.s_addr && ipv4_callback)
165 if (!((*ipv4_callback)(daemon, addr, ifa->ifa_index, netmask, broadcast, parm)))
166 return 0;
167 }
168#ifdef HAVE_IPV6
169 else if (ifa->ifa_family == AF_INET6)
170 {
171 struct in6_addr *addrp = NULL;
172 while (RTA_OK(rta, len1))
173 {
174 if (rta->rta_type == IFA_ADDRESS)
175 addrp = ((struct in6_addr *)(rta+1));
176
177 rta = RTA_NEXT(rta, len1);
178 }
179
180 if (addrp && ipv6_callback)
181 if (!((*ipv6_callback)(daemon, addrp, ifa->ifa_index, ifa->ifa_index, parm)))
182 return 0;
183 }
184#endif
185 }
0a852541 186 }
5e9e0efb 187}
0a852541 188
5e9e0efb
SK
189void netlink_multicast(struct daemon *daemon)
190{
191 ssize_t len;
192 struct nlmsghdr *h;
0a852541 193
5e9e0efb 194 if ((len = netlink_recv(daemon)) != -1)
0a852541 195 {
5e9e0efb
SK
196 for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
197 if (h->nlmsg_type == NLMSG_ERROR)
198 nl_err(h);
199 else
200 nl_routechange(daemon, h);
0a852541 201 }
0a852541
SK
202}
203
5e9e0efb
SK
204static void nl_err(struct nlmsghdr *h)
205{
206 struct nlmsgerr *err = NLMSG_DATA(h);
207 if (err->error != 0)
208 syslog(LOG_ERR, _("RTnetlink returns error: %s"), strerror(-(err->error)));
209}
cdeda28f 210
5e9e0efb 211/* We arrange to receive netlink multicast messages whenever the network route is added.
cdeda28f
SK
212 If this happens and we still have a DNS packet in the buffer, we re-send it.
213 This helps on DoD links, where frequently the packet which triggers dialling is
214 a DNS query, which then gets lost. By re-sending, we can avoid the lookup
215 failing. */
5e9e0efb 216static void nl_routechange(struct daemon *daemon, struct nlmsghdr *h)
cdeda28f 217{
5e9e0efb 218 if (h->nlmsg_type == RTM_NEWROUTE && daemon->srv_save)
cdeda28f 219 {
5e9e0efb
SK
220 struct rtmsg *rtm = NLMSG_DATA(h);
221 if (rtm->rtm_type == RTN_UNICAST &&
222 rtm->rtm_scope == RT_SCOPE_LINK)
223 while(sendto(daemon->srv_save->sfd->fd, daemon->packet, daemon->packet_len, 0,
224 &daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send());
cdeda28f
SK
225 }
226}
227
5e9e0efb
SK
228void arp_inject(int fd, struct in_addr ip_addr, int iface,
229 unsigned char *mac, unsigned int mac_len)
230{
231 struct sockaddr_nl addr;
232 struct {
233 struct nlmsghdr nlh;
234 struct ndmsg m;
235 struct rtattr addr_attr;
236 struct in_addr addr;
237 struct rtattr ll_attr;
238 char mac[DHCP_CHADDR_MAX];
239 } req;
240
241 memset(&req, 0, sizeof(req));
242 memset(&addr, 0, sizeof(addr));
243
244 addr.nl_family = AF_NETLINK;
245
246 req.nlh.nlmsg_len = sizeof(req);
247 req.nlh.nlmsg_type = RTM_NEWNEIGH;
248 req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE;
249
250 req.m.ndm_family = AF_INET;
251 req.m.ndm_ifindex = iface;
252 req.m.ndm_state = NUD_REACHABLE;
253
254 req.addr_attr.rta_type = NDA_DST;
255 req.addr_attr.rta_len = RTA_LENGTH(sizeof(struct in_addr));
256 req.addr = ip_addr;
257
258 req.ll_attr.rta_type = NDA_LLADDR;
259 req.ll_attr.rta_len = RTA_LENGTH(mac_len);
260 memcpy(req.mac, mac, mac_len);
261
262 while(sendto(fd, (void *)&req, sizeof(req), 0, (struct sockaddr *)&addr, sizeof(addr)) == -1 &&
263 retry_send());
264}
265
0a852541
SK
266#endif
267
268