1/*
2 * Copyright (C) International Business Machines  Corp., 2003
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the project nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30/* Author: Shirley Ma, xma@us.ibm.com */
31
32#include <string.h>
33#include <time.h>
34#include <unistd.h>
35#include <malloc.h>
36#include <errno.h>
37#include <syslog.h>
38#include <sys/types.h>
39#include <sys/socket.h>
40#include <asm/types.h>
41#include <linux/netlink.h>
42#include <linux/rtnetlink.h>
43#include <netinet/in.h>
44#include <arpa/inet.h>
45
46#include "queue.h"
47#include "dhcp6.h"
48#include "config.h"
49#include "common.h"
50
51static void
52get_if_prefix(struct nlmsghdr *nlm, int nlm_len, int request,
53	      struct dhcp6_if *ifp)
54{
55	struct rtmsg *rtm = (struct rtmsg *)NLMSG_DATA(nlm);
56	struct rtattr *rta;
57	size_t rtasize, rtapayload;
58	void *rtadata;
59	struct ra_info *rainfo;
60	char addr[64];
61
62	if (rtm->rtm_family != AF_INET6 || nlm->nlmsg_type != request)
63		return;
64
65	if (!(rtm->rtm_flags & RTM_F_PREFIX))
66		return;
67
68	rtasize = NLMSG_PAYLOAD(nlm, nlm_len) - NLMSG_ALIGN(sizeof(*rtm));
69	rta = (struct rtattr *) (((char *) NLMSG_DATA(nlm)) +
70	      NLMSG_ALIGN(sizeof(*rtm)));
71	if (!RTA_OK(rta, rtasize))
72		return;
73	rtadata = RTA_DATA(rta);
74	rtapayload = RTA_PAYLOAD(rta);
75	switch(rta->rta_type) {
76	case RTA_OIF:
77		break;
78	default:
79		break;
80	}
81	switch (rta->rta_type) {
82	case RTA_DST:
83		rainfo = (struct ra_info *)malloc(sizeof(*rainfo));
84		if (rainfo == NULL)
85			return;
86		memset(rainfo, 0, sizeof(rainfo));
87		memcpy(&(rainfo->prefix), (struct in6_addr *)rtadata,
88		       sizeof(struct in6_addr));
89		rainfo->plen = rtm->rtm_dst_len;
90		if (ifp->ralist == NULL) {
91			ifp->ralist = rainfo;
92			rainfo->next = NULL;
93		} else {
94			struct ra_info *ra, *ra_prev;
95			ra_prev = ifp->ralist;
96			for (ra = ifp->ralist; ra; ra = ra->next) {
97				if (rainfo->plen >= ra->plen) {
98					if (ra_prev == ra) {
99						ifp->ralist = rainfo;
100						rainfo->next = ra;
101					} else {
102						ra_prev->next = rainfo;
103						rainfo->next = ra;
104					}
105					break;
106				} else {
107					if (ra->next == NULL) {
108						ra->next = rainfo;
109						rainfo->next = NULL;
110						break;
111					} else {
112						ra_prev = ra;
113						continue;
114					}
115				}
116			}
117		}
118		inet_ntop(AF_INET6, &(rainfo->prefix), addr, INET6_ADDRSTRLEN);
119		dprintf(LOG_DEBUG, "get prefix address %s", addr);
120		dprintf(LOG_DEBUG, "get prefix plen %d",rtm->rtm_dst_len);
121		break;
122	case RTA_CACHEINFO:
123		dprintf(LOG_DEBUG, "prefix route life time is %d\n",
124		      ((struct rta_cacheinfo *)rtadata)->rta_expires);
125		break;
126	default:
127		break;
128	}
129	return;
130}
131
132static void
133get_if_flags(struct nlmsghdr *nlm, int nlm_len, int request,
134	     struct dhcp6_if *ifp)
135{
136	struct ifinfomsg *ifim = (struct ifinfomsg *)NLMSG_DATA(nlm);
137	struct rtattr *rta, *rta1;
138	size_t rtasize, rtasize1, rtapayload;
139	void *rtadata;
140
141	dprintf(LOG_DEBUG, "get_if_flags called");
142
143	if (ifim->ifi_family != AF_INET6 || nlm->nlmsg_type != request)
144		return;
145	if (ifim->ifi_index != ifp->ifid)
146		return;
147	rtasize = NLMSG_PAYLOAD(nlm, nlm_len) - NLMSG_ALIGN(sizeof(*ifim));
148	for (rta = (struct rtattr *) (((char *) NLMSG_DATA(nlm)) +
149	     NLMSG_ALIGN(sizeof(*ifim))); RTA_OK(rta, rtasize);
150	     rta = RTA_NEXT(rta, rtasize)) {
151	rtadata = RTA_DATA(rta);
152	rtapayload = RTA_PAYLOAD(rta);
153
154	switch(rta->rta_type) {
155	case IFLA_IFNAME:
156		break;
157#ifdef IFLA_PROTINFO
158	case IFLA_PROTINFO:
159		rtasize1 = rta->rta_len;
160		for (rta1 = (struct rtattr *)rtadata; RTA_OK(rta1, rtasize1);
161		     rta1 = RTA_NEXT(rta1, rtasize1)) {
162			void *rtadata1 = RTA_DATA(rta1);
163			size_t rtapayload1= RTA_PAYLOAD(rta1);
164			switch(rta1->rta_type) {
165			case IFLA_INET6_CACHEINFO:
166				break;
167			case IFLA_INET6_FLAGS:
168				/* flags for IF_RA_MANAGED/IF_RA_OTHERCONF */
169				ifp->ra_flag = *((u_int32_t *)rtadata1);
170				if (*((u_int32_t *)rtadata1) & IF_RA_MANAGED)
171					dprintf(LOG_DEBUG,
172						"interface managed flags set");
173				if (*((u_int32_t *)rtadata1) & IF_RA_OTHERCONF)
174					dprintf(LOG_DEBUG,
175						"interface otherconf flags set");
176				break;
177			default:
178				break;
179			}
180		}
181		break;
182#endif
183	default:
184		break;
185	}
186	}
187	return;
188}
189
190static int
191open_netlink_socket()
192{
193	struct sockaddr_nl nl_addr;
194	int sd;
195
196	dprintf(LOG_DEBUG, "open_netlink_socket called");
197	sd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
198	if (sd < 0)
199		return -1;
200	memset(&nl_addr, 0, sizeof(nl_addr));
201	nl_addr.nl_family = AF_NETLINK;
202	if (bind(sd, (struct sockaddr *)&nl_addr, sizeof(nl_addr)) < 0) {
203		dprintf(LOG_ERR, "netlink bind error");
204		close(sd);
205		return -1;
206	}
207	return sd;
208}
209
210static int
211netlink_send_rtmsg(int sd, int request, int flags, int seq)
212{
213	struct sockaddr_nl nl_addr;
214	struct nlmsghdr *nlm_hdr;
215	struct rtmsg *rt_msg;
216	char buf[NLMSG_ALIGN (sizeof (struct nlmsghdr)) +
217		  NLMSG_ALIGN (sizeof (struct rtmsg))];
218	int status;
219
220	memset(&buf, 0, sizeof(buf));
221	dprintf(LOG_DEBUG, "netlink_send_rtmsg called");
222
223	nlm_hdr = (struct nlmsghdr *)buf;
224	nlm_hdr->nlmsg_len = NLMSG_LENGTH (sizeof (*rt_msg));
225	nlm_hdr->nlmsg_type = request;
226	nlm_hdr->nlmsg_flags = flags | NLM_F_REQUEST;
227	nlm_hdr->nlmsg_pid = getpid();
228	nlm_hdr->nlmsg_seq = seq;
229
230	memset(&nl_addr, 0, sizeof(nl_addr));
231	nl_addr.nl_family = AF_NETLINK;
232
233	rt_msg = (struct rtmsg *)NLMSG_DATA(nlm_hdr);
234	rt_msg->rtm_family = AF_INET6;
235	rt_msg->rtm_flags = 0;
236	rt_msg->rtm_flags |= RTM_F_PREFIX;
237
238	status = sendto(sd, (void *)nlm_hdr, nlm_hdr->nlmsg_len, 0,
239		(struct sockaddr *)&nl_addr, sizeof(nl_addr));
240	return status;
241}
242
243static int
244netlink_send_rtgenmsg(int sd, int request, int flags, int seq)
245{
246	struct sockaddr_nl nl_addr;
247	struct nlmsghdr *nlm_hdr;
248	struct rtgenmsg *rt_genmsg;
249	char buf[NLMSG_ALIGN (sizeof (struct nlmsghdr)) +
250		  NLMSG_ALIGN (sizeof (struct rtgenmsg))];
251	int status;
252
253	memset(&buf, 0, sizeof(buf));
254	dprintf(LOG_DEBUG, "netlink_send_rtgenmsg called");
255
256	nlm_hdr = (struct nlmsghdr *)buf;
257	nlm_hdr->nlmsg_len = NLMSG_LENGTH (sizeof (*rt_genmsg));
258	nlm_hdr->nlmsg_type = request;
259	nlm_hdr->nlmsg_flags = flags | NLM_F_REQUEST;
260	nlm_hdr->nlmsg_pid = getpid();
261	nlm_hdr->nlmsg_seq = seq;
262
263	memset(&nl_addr, 0, sizeof(nl_addr));
264	nl_addr.nl_family = AF_NETLINK;
265
266	rt_genmsg = (struct rtgenmsg*)NLMSG_DATA(nlm_hdr);
267	rt_genmsg->rtgen_family = AF_INET6;
268	status = sendto(sd, (void *)nlm_hdr, nlm_hdr->nlmsg_len, 0,
269		(struct sockaddr *)&nl_addr, sizeof(nl_addr));
270	return status;
271}
272
273static int
274netlink_recv_rtgenmsg(int sd, int request, int seq, struct dhcp6_if *ifp)
275{
276	struct nlmsghdr *nlm;
277	struct msghdr msgh;
278	struct sockaddr_nl nl_addr;
279	char *buf = NULL;
280	size_t newsize = 65536, size = 0;
281	int msg_len;
282
283	dprintf(LOG_DEBUG, "netlink_recv_rtgenmsg called");
284
285	if (seq == 0)
286		seq = (int)time(NULL);
287	for (;;) {
288		void *newbuf = realloc(buf, newsize);
289		if (newbuf == NULL) {
290			msg_len = -1;
291			break;
292		}
293		buf = newbuf;
294		do {
295			struct iovec iov = {buf, newsize};
296			memset(&msgh, 0, sizeof(msgh));
297			msgh.msg_name = (void *)&nl_addr;
298			msgh.msg_namelen = sizeof(nl_addr);
299			msgh.msg_iov = &iov;
300			msgh.msg_iovlen = 1;
301			msg_len = recvmsg(sd, &msgh, 0);
302		} while (msg_len < 0 && errno == EINTR);
303
304		if (msg_len < 0 || msgh.msg_flags & MSG_TRUNC) {
305			size = newsize;
306			newsize *= 2;
307			continue;
308		} else if (msg_len == 0)
309			break;
310
311		/* buf might have some data not for this request */
312		for (nlm = (struct nlmsghdr *)buf; NLMSG_OK(nlm, msg_len);
313		     nlm = (struct nlmsghdr *)NLMSG_NEXT(nlm, msg_len)) {
314			if (nlm->nlmsg_type == NLMSG_DONE ||
315			    nlm->nlmsg_type == NLMSG_ERROR) {
316				dprintf(LOG_ERR, "netlink_recv_rtgenmsg error");
317				goto out;
318			}
319			if (nlm->nlmsg_pid != getpid() ||
320			    nlm->nlmsg_seq != seq)
321				continue;
322			if (request == RTM_NEWROUTE)
323				get_if_prefix(nlm, msg_len, request, ifp);
324			if (request == RTM_NEWLINK)
325				get_if_flags(nlm, msg_len, request, ifp);
326		}
327		free(buf);
328		buf = NULL;
329	}
330out:	if (buf)
331		free(buf);
332	return msg_len;
333}
334
335int
336get_if_rainfo(struct dhcp6_if *ifp)
337{
338	int sd, status;
339	int seq = time(NULL);
340
341	sd = open_netlink_socket();
342	if (sd < 0)
343		return sd;
344	status = netlink_send_rtmsg(sd, RTM_GETROUTE, NLM_F_ROOT, seq);
345	if (status >= 0)
346		status = netlink_recv_rtgenmsg(sd, RTM_NEWROUTE, seq, ifp);
347	else
348		goto out;
349	status = netlink_send_rtgenmsg(sd, RTM_GETLINK, NLM_F_ROOT, seq);
350	if (status >= 0)
351		status = netlink_recv_rtgenmsg(sd, RTM_NEWLINK, seq, ifp);
352out:	close(sd);
353	return status;
354}
355