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