1/* 2 * kernel routing table update by ioctl(). 3 * Copyright (C) 1997, 98 Kunihiro Ishiguro 4 * 5 * This file is part of GNU Zebra. 6 * 7 * GNU Zebra is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the 9 * Free Software Foundation; either version 2, or (at your option) any 10 * later version. 11 * 12 * GNU Zebra is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with GNU Zebra; see the file COPYING. If not, write to the Free 19 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 20 * 02111-1307, USA. 21 */ 22 23#include <zebra.h> 24 25#include "prefix.h" 26#include "log.h" 27#include "if.h" 28 29#include "zebra/rib.h" 30#include "zebra/debug.h" 31 32/* Initialize of kernel interface. There is no kernel communication 33 support under ioctl(). So this is dummy stub function. */ 34void 35kernel_init () 36{ 37 return; 38} 39 40/* Dummy function of routing socket. */ 41void 42kernel_read (int sock) 43{ 44 return; 45} 46 47#if 0 48/* Initialization prototype of struct sockaddr_in. */ 49static struct sockaddr_in sin_proto = 50{ 51#ifdef HAVE_SIN_LEN 52 sizeof (struct sockaddr_in), 53#endif /* HAVE_SIN_LEN */ 54 AF_INET, 0, {0}, {0} 55}; 56#endif /* 0 */ 57 58/* Solaris has ortentry. */ 59#ifdef HAVE_OLD_RTENTRY 60#define rtentry ortentry 61#endif /* HAVE_OLD_RTENTRY */ 62 63/* Interface to ioctl route message. */ 64int 65kernel_add_route (struct prefix_ipv4 *dest, struct in_addr *gate, 66 int index, int flags) 67{ 68 int ret; 69 int sock; 70 struct rtentry rtentry; 71 struct sockaddr_in sin_dest, sin_mask, sin_gate; 72 73 memset (&rtentry, 0, sizeof (struct rtentry)); 74 75 /* Make destination. */ 76 memset (&sin_dest, 0, sizeof (struct sockaddr_in)); 77 sin_dest.sin_family = AF_INET; 78#ifdef HAVE_SIN_LEN 79 sin_dest.sin_len = sizeof (struct sockaddr_in); 80#endif /* HAVE_SIN_LEN */ 81 sin_dest.sin_addr = dest->prefix; 82 83 /* Make gateway. */ 84 if (gate) 85 { 86 memset (&sin_gate, 0, sizeof (struct sockaddr_in)); 87 sin_gate.sin_family = AF_INET; 88#ifdef HAVE_SIN_LEN 89 sin_gate.sin_len = sizeof (struct sockaddr_in); 90#endif /* HAVE_SIN_LEN */ 91 sin_gate.sin_addr = *gate; 92 } 93 94 memset (&sin_mask, 0, sizeof (struct sockaddr_in)); 95 sin_mask.sin_family = AF_INET; 96#ifdef HAVE_SIN_LEN 97 sin_gate.sin_len = sizeof (struct sockaddr_in); 98#endif /* HAVE_SIN_LEN */ 99 masklen2ip (dest->prefixlen, &sin_mask.sin_addr); 100 101 /* Set destination address, mask and gateway.*/ 102 memcpy (&rtentry.rt_dst, &sin_dest, sizeof (struct sockaddr_in)); 103 if (gate) 104 memcpy (&rtentry.rt_gateway, &sin_gate, sizeof (struct sockaddr_in)); 105#ifndef SUNOS_5 106 memcpy (&rtentry.rt_genmask, &sin_mask, sizeof (struct sockaddr_in)); 107#endif /* SUNOS_5 */ 108 109 /* Routing entry flag set. */ 110 if (dest->prefixlen == 32) 111 rtentry.rt_flags |= RTF_HOST; 112 113 if (gate && gate->s_addr != INADDR_ANY) 114 rtentry.rt_flags |= RTF_GATEWAY; 115 116 rtentry.rt_flags |= RTF_UP; 117 118 /* Additional flags */ 119 rtentry.rt_flags |= flags; 120 121 122 /* For tagging route. */ 123 /* rtentry.rt_flags |= RTF_DYNAMIC; */ 124 125 /* Open socket for ioctl. */ 126 sock = socket (AF_INET, SOCK_DGRAM, 0); 127 if (sock < 0) 128 { 129 zlog_warn ("can't make socket\n"); 130 return -1; 131 } 132 133 /* Send message by ioctl(). */ 134 ret = ioctl (sock, SIOCADDRT, &rtentry); 135 if (ret < 0) 136 { 137 switch (errno) 138 { 139 case EEXIST: 140 close (sock); 141 return ZEBRA_ERR_RTEXIST; 142 break; 143 case ENETUNREACH: 144 close (sock); 145 return ZEBRA_ERR_RTUNREACH; 146 break; 147 case EPERM: 148 close (sock); 149 return ZEBRA_ERR_EPERM; 150 break; 151 } 152 153 close (sock); 154 zlog_warn ("write : %s (%d)", strerror (errno), errno); 155 return 1; 156 } 157 close (sock); 158 159 return ret; 160} 161 162/* Interface to ioctl route message. */ 163int 164kernel_ioctl_ipv4 (u_long cmd, struct prefix *p, struct rib *rib, int family) 165{ 166 int ret; 167 int sock; 168 struct rtentry rtentry; 169 struct sockaddr_in sin_dest, sin_mask, sin_gate; 170 struct nexthop *nexthop; 171 int nexthop_num = 0; 172 struct interface *ifp; 173 174 memset (&rtentry, 0, sizeof (struct rtentry)); 175 176 /* Make destination. */ 177 memset (&sin_dest, 0, sizeof (struct sockaddr_in)); 178 sin_dest.sin_family = AF_INET; 179#ifdef HAVE_SIN_LEN 180 sin_dest.sin_len = sizeof (struct sockaddr_in); 181#endif /* HAVE_SIN_LEN */ 182 sin_dest.sin_addr = p->u.prefix4; 183 184 memset (&sin_gate, 0, sizeof (struct sockaddr_in)); 185 186 /* Make gateway. */ 187 for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) 188 { 189 if ((cmd == SIOCADDRT 190 && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) 191 || (cmd == SIOCDELRT 192 && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) 193 { 194 if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) 195 { 196 if (nexthop->rtype == NEXTHOP_TYPE_IPV4 || 197 nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX) 198 { 199 sin_gate.sin_family = AF_INET; 200#ifdef HAVE_SIN_LEN 201 sin_gate.sin_len = sizeof (struct sockaddr_in); 202#endif /* HAVE_SIN_LEN */ 203 sin_gate.sin_addr = nexthop->rgate.ipv4; 204 rtentry.rt_flags |= RTF_GATEWAY; 205 } 206 if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX 207 || nexthop->rtype == NEXTHOP_TYPE_IFNAME) 208 { 209 ifp = if_lookup_by_index (nexthop->rifindex); 210 if (ifp) 211 rtentry.rt_dev = ifp->name; 212 else 213 return -1; 214 } 215 } 216 else 217 { 218 if (nexthop->type == NEXTHOP_TYPE_IPV4 || 219 nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) 220 { 221 sin_gate.sin_family = AF_INET; 222#ifdef HAVE_SIN_LEN 223 sin_gate.sin_len = sizeof (struct sockaddr_in); 224#endif /* HAVE_SIN_LEN */ 225 sin_gate.sin_addr = nexthop->gate.ipv4; 226 rtentry.rt_flags |= RTF_GATEWAY; 227 } 228 if (nexthop->type == NEXTHOP_TYPE_IFINDEX 229 || nexthop->type == NEXTHOP_TYPE_IFNAME) 230 { 231 ifp = if_lookup_by_index (nexthop->ifindex); 232 if (ifp) 233 rtentry.rt_dev = ifp->name; 234 else 235 return -1; 236 } 237 } 238 239 if (cmd == SIOCADDRT) 240 SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); 241 242 nexthop_num++; 243 break; 244 } 245 } 246 247 /* If there is no useful nexthop then return. */ 248 if (nexthop_num == 0) 249 { 250 if (IS_ZEBRA_DEBUG_KERNEL) 251 zlog_info ("netlink_route_multipath(): No useful nexthop."); 252 return 0; 253 } 254 255 memset (&sin_mask, 0, sizeof (struct sockaddr_in)); 256 sin_mask.sin_family = AF_INET; 257#ifdef HAVE_SIN_LEN 258 sin_mask.sin_len = sizeof (struct sockaddr_in); 259#endif /* HAVE_SIN_LEN */ 260 masklen2ip (p->prefixlen, &sin_mask.sin_addr); 261 262 /* Set destination address, mask and gateway.*/ 263 memcpy (&rtentry.rt_dst, &sin_dest, sizeof (struct sockaddr_in)); 264 265 if (rtentry.rt_flags & RTF_GATEWAY) 266 memcpy (&rtentry.rt_gateway, &sin_gate, sizeof (struct sockaddr_in)); 267 268#ifndef SUNOS_5 269 memcpy (&rtentry.rt_genmask, &sin_mask, sizeof (struct sockaddr_in)); 270#endif /* SUNOS_5 */ 271 272 /* Metric. It seems metric minus one value is installed... */ 273 rtentry.rt_metric = rib->metric; 274 275 /* Routing entry flag set. */ 276 if (p->prefixlen == 32) 277 rtentry.rt_flags |= RTF_HOST; 278 279 rtentry.rt_flags |= RTF_UP; 280 281 /* Additional flags */ 282 /* rtentry.rt_flags |= flags; */ 283 284 /* For tagging route. */ 285 /* rtentry.rt_flags |= RTF_DYNAMIC; */ 286 287 /* Open socket for ioctl. */ 288 sock = socket (AF_INET, SOCK_DGRAM, 0); 289 if (sock < 0) 290 { 291 zlog_warn ("can't make socket\n"); 292 return -1; 293 } 294 295 /* Send message by ioctl(). */ 296 ret = ioctl (sock, cmd, &rtentry); 297 if (ret < 0) 298 { 299 switch (errno) 300 { 301 case EEXIST: 302 close (sock); 303 return ZEBRA_ERR_RTEXIST; 304 break; 305 case ENETUNREACH: 306 close (sock); 307 return ZEBRA_ERR_RTUNREACH; 308 break; 309 case EPERM: 310 close (sock); 311 return ZEBRA_ERR_EPERM; 312 break; 313 } 314 315 close (sock); 316 zlog_warn ("write : %s (%d)", strerror (errno), errno); 317 return ret; 318 } 319 close (sock); 320 321 return ret; 322} 323 324int 325kernel_add_ipv4 (struct prefix *p, struct rib *rib) 326{ 327 return kernel_ioctl_ipv4 (SIOCADDRT, p, rib, AF_INET); 328} 329 330int 331kernel_delete_ipv4 (struct prefix *p, struct rib *rib) 332{ 333 return kernel_ioctl_ipv4 (SIOCDELRT, p, rib, AF_INET); 334} 335 336#ifdef HAVE_IPV6 337 338/* Below is hack for GNU libc definition and Linux 2.1.X header. */ 339#undef RTF_DEFAULT 340#undef RTF_ADDRCONF 341 342#include <asm/types.h> 343 344#if defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1 345/* struct in6_rtmsg will be declared in net/route.h. */ 346#else 347#include <linux/ipv6_route.h> 348#endif 349 350int 351kernel_ioctl_ipv6 (u_long type, struct prefix_ipv6 *dest, struct in6_addr *gate, 352 int index, int flags) 353{ 354 int ret; 355 int sock; 356 struct in6_rtmsg rtm; 357 358 memset (&rtm, 0, sizeof (struct in6_rtmsg)); 359 360 rtm.rtmsg_flags |= RTF_UP; 361 rtm.rtmsg_metric = 1; 362 memcpy (&rtm.rtmsg_dst, &dest->prefix, sizeof (struct in6_addr)); 363 rtm.rtmsg_dst_len = dest->prefixlen; 364 365 /* We need link local index. But this should be done caller... 366 if (IN6_IS_ADDR_LINKLOCAL(&rtm.rtmsg_gateway)) 367 { 368 index = if_index_address (&rtm.rtmsg_gateway); 369 rtm.rtmsg_ifindex = index; 370 } 371 else 372 rtm.rtmsg_ifindex = 0; 373 */ 374 375 rtm.rtmsg_flags |= RTF_GATEWAY; 376 377 /* For tagging route. */ 378 /* rtm.rtmsg_flags |= RTF_DYNAMIC; */ 379 380 memcpy (&rtm.rtmsg_gateway, gate, sizeof (struct in6_addr)); 381 382 if (index) 383 rtm.rtmsg_ifindex = index; 384 else 385 rtm.rtmsg_ifindex = 0; 386 387 rtm.rtmsg_metric = 1; 388 389 sock = socket (AF_INET6, SOCK_DGRAM, 0); 390 if (sock < 0) 391 { 392 zlog_warn ("can't make socket\n"); 393 return -1; 394 } 395 396 /* Send message via ioctl. */ 397 ret = ioctl (sock, type, &rtm); 398 if (ret < 0) 399 { 400 zlog_warn ("can't %s ipv6 route: %s\n", type == SIOCADDRT ? "add" : "delete", 401 strerror(errno)); 402 ret = errno; 403 close (sock); 404 return ret; 405 } 406 close (sock); 407 408 return ret; 409} 410 411int 412kernel_ioctl_ipv6_multipath (u_long cmd, struct prefix *p, struct rib *rib, 413 int family) 414{ 415 int ret; 416 int sock; 417 struct in6_rtmsg rtm; 418 struct nexthop *nexthop; 419 int nexthop_num = 0; 420 421 memset (&rtm, 0, sizeof (struct in6_rtmsg)); 422 423 rtm.rtmsg_flags |= RTF_UP; 424 rtm.rtmsg_metric = rib->metric; 425 memcpy (&rtm.rtmsg_dst, &p->u.prefix, sizeof (struct in6_addr)); 426 rtm.rtmsg_dst_len = p->prefixlen; 427 428 /* We need link local index. But this should be done caller... 429 if (IN6_IS_ADDR_LINKLOCAL(&rtm.rtmsg_gateway)) 430 { 431 index = if_index_address (&rtm.rtmsg_gateway); 432 rtm.rtmsg_ifindex = index; 433 } 434 else 435 rtm.rtmsg_ifindex = 0; 436 */ 437 438 rtm.rtmsg_flags |= RTF_GATEWAY; 439 440 /* For tagging route. */ 441 /* rtm.rtmsg_flags |= RTF_DYNAMIC; */ 442 443 /* Make gateway. */ 444 for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) 445 { 446 if ((cmd == SIOCADDRT 447 && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) 448 || (cmd == SIOCDELRT 449 && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) 450 { 451 if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) 452 { 453 if (nexthop->rtype == NEXTHOP_TYPE_IPV6 454 || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME 455 || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX) 456 { 457 memcpy (&rtm.rtmsg_gateway, &nexthop->rgate.ipv6, 458 sizeof (struct in6_addr)); 459 } 460 if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX 461 || nexthop->rtype == NEXTHOP_TYPE_IFNAME 462 || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME 463 || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX) 464 rtm.rtmsg_ifindex = nexthop->rifindex; 465 else 466 rtm.rtmsg_ifindex = 0; 467 468 } 469 else 470 { 471 if (nexthop->type == NEXTHOP_TYPE_IPV6 472 || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME 473 || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) 474 { 475 memcpy (&rtm.rtmsg_gateway, &nexthop->gate.ipv6, 476 sizeof (struct in6_addr)); 477 } 478 if (nexthop->type == NEXTHOP_TYPE_IFINDEX 479 || nexthop->type == NEXTHOP_TYPE_IFNAME 480 || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME 481 || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) 482 rtm.rtmsg_ifindex = nexthop->ifindex; 483 else 484 rtm.rtmsg_ifindex = 0; 485 } 486 487 if (cmd == SIOCADDRT) 488 SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); 489 490 nexthop_num++; 491 break; 492 } 493 } 494 495 /* If there is no useful nexthop then return. */ 496 if (nexthop_num == 0) 497 { 498 if (IS_ZEBRA_DEBUG_KERNEL) 499 zlog_info ("netlink_route_multipath(): No useful nexthop."); 500 return 0; 501 } 502 503 sock = socket (AF_INET6, SOCK_DGRAM, 0); 504 if (sock < 0) 505 { 506 zlog_warn ("can't make socket\n"); 507 return -1; 508 } 509 510 /* Send message via ioctl. */ 511 ret = ioctl (sock, cmd, &rtm); 512 if (ret < 0) 513 { 514 zlog_warn ("can't %s ipv6 route: %s\n", 515 cmd == SIOCADDRT ? "add" : "delete", 516 strerror(errno)); 517 ret = errno; 518 close (sock); 519 return ret; 520 } 521 close (sock); 522 523 return ret; 524} 525 526int 527kernel_add_ipv6 (struct prefix *p, struct rib *rib) 528{ 529 return kernel_ioctl_ipv6_multipath (SIOCADDRT, p, rib, AF_INET6); 530} 531 532int 533kernel_delete_ipv6 (struct prefix *p, struct rib *rib) 534{ 535 return kernel_ioctl_ipv6_multipath (SIOCDELRT, p, rib, AF_INET6); 536} 537 538/* Delete IPv6 route from the kernel. */ 539int 540kernel_delete_ipv6_old (struct prefix_ipv6 *dest, struct in6_addr *gate, 541 int index, int flags, int table) 542{ 543 return kernel_ioctl_ipv6 (SIOCDELRT, dest, gate, index, flags); 544} 545#endif /* HAVE_IPV6 */ 546