/* Interface between zebra message and rtm message. */ static int kernel_rtm_ipv6 (int message, struct prefix_ipv6 *dest, struct in6_addr *gate, int index, int flags) { struct sockaddr_in6 *mask; struct sockaddr_in6 sin_dest, sin_mask, sin_gate; memset (&sin_dest, 0, sizeof (struct sockaddr_in6)); sin_dest.sin6_family = AF_INET6; #ifdef SIN6_LEN sin_dest.sin6_len = sizeof (struct sockaddr_in6); #endif /* SIN6_LEN */ memset (&sin_mask, 0, sizeof (struct sockaddr_in6)); memset (&sin_gate, 0, sizeof (struct sockaddr_in6)); sin_gate.sin6_family = AF_INET6; #ifdef SIN6_LEN sin_gate.sin6_len = sizeof (struct sockaddr_in6); #endif /* SIN6_LEN */ sin_dest.sin6_addr = dest->prefix; if (gate) memcpy (&sin_gate.sin6_addr, gate, sizeof (struct in6_addr)); /* Under kame set interface index to link local address. */ #ifdef KAME #define SET_IN6_LINKLOCAL_IFINDEX(a, i) \ do { \ (a).s6_addr[2] = ((i) >> 8) & 0xff; \ (a).s6_addr[3] = (i) & 0xff; \ } while (0) if (gate && IN6_IS_ADDR_LINKLOCAL(gate)) SET_IN6_LINKLOCAL_IFINDEX (sin_gate.sin6_addr, index); #endif /* KAME */ if (gate && dest->prefixlen == 128) mask = NULL; else { masklen2ip6 (dest->prefixlen, &sin_mask.sin6_addr); sin_mask.sin6_family = AF_INET6; #ifdef SIN6_LEN sin_mask.sin6_len = sin6_masklen (sin_mask.sin6_addr); #endif /* SIN6_LEN */ mask = &sin_mask; } return rtm_write (message, (union sockunion *) &sin_dest, (union sockunion *) mask, gate ? (union sockunion *)&sin_gate : NULL, index, flags, 0); }
/* Interface's address information get. */ int ifam_read (struct ifa_msghdr *ifam) { struct interface *ifp; union sockunion addr, mask, gate; /* Check does this interface exist or not. */ ifp = if_lookup_by_index (ifam->ifam_index); if (ifp == NULL) { zlog_warn ("no interface for index %d", ifam->ifam_index); return -1; } /* Allocate and read address information. */ ifam_read_mesg (ifam, &addr, &mask, &gate); /* Check interface flag for implicit up of the interface. */ if_refresh (ifp); /* Add connected address. */ switch (sockunion_family (&addr)) { case AF_INET: if (ifam->ifam_type == RTM_NEWADDR) connected_add_ipv4 (ifp, 0, &addr.sin.sin_addr, ip_masklen (mask.sin.sin_addr), &gate.sin.sin_addr, NULL); else connected_delete_ipv4 (ifp, 0, &addr.sin.sin_addr, ip_masklen (mask.sin.sin_addr), &gate.sin.sin_addr, NULL); break; #ifdef HAVE_IPV6 case AF_INET6: /* Unset interface index from link-local address when IPv6 stack is KAME. */ if (IN6_IS_ADDR_LINKLOCAL (&addr.sin6.sin6_addr)) SET_IN6_LINKLOCAL_IFINDEX (addr.sin6.sin6_addr, 0); if (ifam->ifam_type == RTM_NEWADDR) connected_add_ipv6 (ifp, &addr.sin6.sin6_addr, ip6_masklen (mask.sin6.sin6_addr), &gate.sin6.sin6_addr); else connected_delete_ipv6 (ifp, &addr.sin6.sin6_addr, ip6_masklen (mask.sin6.sin6_addr), &gate.sin6.sin6_addr); break; #endif /* HAVE_IPV6 */ default: /* Unsupported family silently ignore... */ break; } return 0; }
static int parse_kernel_route(const struct rt_msghdr *rtm, struct kernel_route *route) { void *rta = (void*)rtm + sizeof(struct rt_msghdr); struct sockaddr_in6 *sin6; char addr[INET6_ADDRSTRLEN]; memset(route, 0, sizeof(*route)); route->metric = 0; route->ifindex = rtm->rtm_index; if(!(rtm->rtm_addrs & RTA_DST)) return -1; sin6 = (struct sockaddr_in6 *)rta; if(IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&sin6->sin6_addr)) return -1; if((rtm->rtm_flags & RTF_PROTO2) != 0) return -1; memcpy(&route->prefix, &sin6->sin6_addr, 16); rta += ROUNDUP(sizeof(struct sockaddr_in6)); if(!(rtm->rtm_addrs & RTA_GATEWAY)) return -1; sin6 = (struct sockaddr_in6 *)rta; if(IN6_IS_ADDR_LINKLOCAL (&sin6->sin6_addr)) { route->ifindex = IN6_LINKLOCAL_IFINDEX(sin6->sin6_addr); SET_IN6_LINKLOCAL_IFINDEX(sin6->sin6_addr, 0); } memcpy(&route->gw, &sin6->sin6_addr, 16); rta += ROUNDUP(sizeof(struct sockaddr_in6)); if(!(rtm->rtm_addrs & RTA_NETMASK)) { route->plen = 0; } else { sin6 = (struct sockaddr_in6 *)rta; route->plen = mask2len(&sin6->sin6_addr); inet_ntop(AF_INET6, &sin6->sin6_addr, addr, sizeof(addr)); rta += ROUNDUP(sizeof(struct sockaddr_in6)); } if (rtm->rtm_flags & RTF_HOST) route->plen = 128; if(ifindex_lo < 0) { ifindex_lo = if_nametoindex("lo0"); if(ifindex_lo <= 0) return -1; } if(route->ifindex == ifindex_lo) return -1; return 0; }
/* Performs a non-blocking connect(). */ enum connect_result sockunion_connect(int fd, const union sockunion *peersu, unsigned short port, ifindex_t ifindex) { int ret; union sockunion su; memcpy(&su, peersu, sizeof(union sockunion)); switch (su.sa.sa_family) { case AF_INET: su.sin.sin_port = port; break; case AF_INET6: su.sin6.sin6_port = port; #ifdef KAME if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) && ifindex) { su.sin6.sin6_scope_id = ifindex; SET_IN6_LINKLOCAL_IFINDEX(su.sin6.sin6_addr, ifindex); } #endif /* KAME */ break; } /* Call connect function. */ ret = connect(fd, (struct sockaddr *)&su, sockunion_sizeof(&su)); /* Immediate success */ if (ret == 0) return connect_success; /* If connect is in progress then return 1 else it's real error. */ if (ret < 0) { if (errno != EINPROGRESS) { char str[SU_ADDRSTRLEN]; zlog_info("can't connect to %s fd %d : %s", sockunion_log(&su, str, sizeof str), fd, safe_strerror(errno)); return connect_error; } } return connect_in_progress; }
void rtm_read (struct rt_msghdr *rtm) { int flags; u_char zebra_flags; union sockunion dest, mask, gate; zebra_flags = 0; /* Discard self send message. */ if (rtm->rtm_type != RTM_GET && (rtm->rtm_pid == pid || rtm->rtm_pid == old_pid)) return; /* Read destination and netmask and gateway from rtm message structure. */ flags = rtm_read_mesg (rtm, &dest, &mask, &gate); #ifdef RTF_CLONED /*bsdi, netbsd 1.6*/ if (flags & RTF_CLONED) return; #endif #ifdef RTF_WASCLONED /*freebsd*/ if (flags & RTF_WASCLONED) return; #endif if ((rtm->rtm_type == RTM_ADD) && ! (flags & RTF_UP)) return; /* Ignore route which has both HOST and GATEWAY attribute. */ if ((flags & RTF_GATEWAY) && (flags & RTF_HOST)) return; /* This is connected route. */ if (! (flags & RTF_GATEWAY)) return; if (flags & RTF_PROTO1) SET_FLAG (zebra_flags, ZEBRA_FLAG_SELFROUTE); /* This is persistent route. */ if (flags & RTF_STATIC) SET_FLAG (zebra_flags, ZEBRA_FLAG_STATIC); if (dest.sa.sa_family == AF_INET) { struct prefix_ipv4 p; p.family = AF_INET; p.prefix = dest.sin.sin_addr; p.prefixlen = ip_masklen (mask.sin.sin_addr); if (rtm->rtm_type == RTM_GET || rtm->rtm_type == RTM_ADD) rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, &gate.sin.sin_addr, 0, 0, 0, 0); else rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, &gate.sin.sin_addr, 0, 0); } #ifdef HAVE_IPV6 if (dest.sa.sa_family == AF_INET6) { struct prefix_ipv6 p; unsigned int ifindex = 0; p.family = AF_INET6; p.prefix = dest.sin6.sin6_addr; p.prefixlen = ip6_masklen (mask.sin6.sin6_addr); #ifdef KAME if (IN6_IS_ADDR_LINKLOCAL (&gate.sin6.sin6_addr)) { ifindex = IN6_LINKLOCAL_IFINDEX (gate.sin6.sin6_addr); SET_IN6_LINKLOCAL_IFINDEX (gate.sin6.sin6_addr, 0); } #endif /* KAME */ if (rtm->rtm_type == RTM_GET || rtm->rtm_type == RTM_ADD) rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, &gate.sin6.sin6_addr, ifindex, 0); else rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, &gate.sin6.sin6_addr, ifindex, 0); } #endif /* HAVE_IPV6 */ }
/* sockunion_connect returns -1 : error occured 0 : connect success 1 : connect is in progress */ enum connect_result sockunion_connect (int fd, union sockunion *peersu, unsigned short port, unsigned int ifindex) { int ret; int val; union sockunion su; memcpy (&su, peersu, sizeof (union sockunion)); switch (su.sa.sa_family) { case AF_INET: su.sin.sin_port = port; break; #ifdef HAVE_IPV6 case AF_INET6: su.sin6.sin6_port = port; #ifdef KAME if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) && ifindex) { #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID /* su.sin6.sin6_scope_id = ifindex; */ #ifdef MUSICA su.sin6.sin6_scope_id = ifindex; #endif #endif /* HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID */ #ifndef MUSICA SET_IN6_LINKLOCAL_IFINDEX (su.sin6.sin6_addr, ifindex); #endif } #endif /* KAME */ break; #endif /* HAVE_IPV6 */ } /* Make socket non-block. */ val = fcntl (fd, F_GETFL, 0); fcntl (fd, F_SETFL, val|O_NONBLOCK); /* Call connect function. */ ret = connect (fd, (struct sockaddr *) &su, sockunion_sizeof (&su)); /* Immediate success */ if (ret == 0) { fcntl (fd, F_SETFL, val); return connect_success; } /* If connect is in progress then return 1 else it's real error. */ if (ret < 0) { if (errno != EINPROGRESS) { char str[SU_ADDRSTRLEN]; zlog_info ("can't connect to %s fd %d : %s", sockunion_log (&su, str, sizeof str), fd, safe_strerror (errno)); return connect_error; } } fcntl (fd, F_SETFL, val); return connect_in_progress; }
/* Tries to open TCP Connection to BGP Peer */ void bpn_sock_cb_connect (struct bgp_peer *peer) { pal_sock_handle_t sck_fd; union sockunion su; u_int32_t ifindex; s_int32_t ret; ifindex = 0; /* Obtain Socket FD */ sck_fd = stream_sock_cb_get_fd (peer->sock_cb, &peer->su, &BLG); if (sck_fd < 0) { BGP_PEER_FSM_EVENT_ADD (&BLG, peer, BPF_EVENT_TCP_CONN_FAIL); goto EXIT; } /* Set Socket options */ ret = bpn_sock_set_opt (peer, sck_fd, PAL_TRUE); if (ret < 0) { BGP_PEER_FSM_EVENT_ADD (&BLG, peer, BPF_EVENT_TCP_CONN_FAIL); goto EXIT; } /* Prepare the Sock-Union struct to connect */ pal_mem_cpy (&su, &peer->su, sizeof (union sockunion)); #ifdef HAVE_IPV6 IF_BGP_CAP_HAVE_IPV6 { if (peer->ifname) ifindex = if_name2index (&BGP_VR.owning_ivr->ifm, peer->ifname); } #endif /* HAVE_IPV6 */ switch (su.sa.sa_family) { case AF_INET: if (peer->sock_remote_port != BGP_PORT_DEFAULT) su.sin.sin_port = pal_hton16 (peer->sock_remote_port); else su.sin.sin_port = pal_hton16 (BGP_PORT_DEFAULT); break; #ifdef HAVE_IPV6 case AF_INET6: if (peer->sock_remote_port != BGP_PORT_DEFAULT) su.sin.sin_port = pal_hton16 (peer->sock_remote_port); else su.sin6.sin6_port = pal_hton16 (BGP_PORT_DEFAULT); #ifdef KAME if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) && ifindex) { SET_IN6_LINKLOCAL_IFINDEX (su.sin6.sin6_addr, ifindex); } #elif defined(HAVE_SIN6_SCOPE_ID) if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) && ifindex) { su.sin6.sin6_scope_id = ifindex; SET_IN6_LINKLOCAL_IFINDEX (su.sin6.sin6_addr, ifindex); } #endif /* HAVE_SIN6_SCOPE_ID */ break; #endif /* HAVE_IPV6 */ } /* Connect the Stream Socket */ ret = stream_sock_cb_connect (peer->sock_cb, &su, &BLG); if (ret < 0) { BGP_PEER_FSM_EVENT_ADD (&BLG, peer, BPF_EVENT_TCP_CONN_FAIL); goto EXIT; } EXIT: return; }
void rtm_read (struct rt_msghdr *rtm) { int flags; u_char zebra_flags; union sockunion dest, mask, gate; char ifname[INTERFACE_NAMSIZ + 1]; short ifnlen = 0; zebra_flags = 0; /* Read destination and netmask and gateway from rtm message structure. */ flags = rtm_read_mesg (rtm, &dest, &mask, &gate, ifname, &ifnlen); if (!(flags & RTF_DONE)) return; if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug ("%s: got rtm of type %d (%s)", __func__, rtm->rtm_type, lookup (rtm_type_str, rtm->rtm_type)); #ifdef RTF_CLONED /*bsdi, netbsd 1.6*/ if (flags & RTF_CLONED) return; #endif #ifdef RTF_WASCLONED /*freebsd*/ if (flags & RTF_WASCLONED) return; #endif if ((rtm->rtm_type == RTM_ADD) && ! (flags & RTF_UP)) return; /* This is connected route. */ if (! (flags & RTF_GATEWAY)) return; if (flags & RTF_PROTO1) SET_FLAG (zebra_flags, ZEBRA_FLAG_SELFROUTE); /* This is persistent route. */ if (flags & RTF_STATIC) SET_FLAG (zebra_flags, ZEBRA_FLAG_STATIC); /* This is a reject or blackhole route */ if (flags & RTF_REJECT) SET_FLAG (zebra_flags, ZEBRA_FLAG_REJECT); if (flags & RTF_BLACKHOLE) SET_FLAG (zebra_flags, ZEBRA_FLAG_BLACKHOLE); if (dest.sa.sa_family == AF_INET) { struct prefix_ipv4 p; p.family = AF_INET; p.prefix = dest.sin.sin_addr; if (flags & RTF_HOST) p.prefixlen = IPV4_MAX_PREFIXLEN; else p.prefixlen = ip_masklen (mask.sin.sin_addr); /* Catch self originated messages and match them against our current RIB. * At the same time, ignore unconfirmed messages, they should be tracked * by rtm_write() and kernel_rtm_ipv4(). */ if (rtm->rtm_type != RTM_GET && rtm->rtm_pid == pid) { char buf[INET_ADDRSTRLEN], gate_buf[INET_ADDRSTRLEN]; int ret; if (! IS_ZEBRA_DEBUG_RIB) return; ret = rib_lookup_ipv4_route (&p, &gate); inet_ntop (AF_INET, &p.prefix, buf, INET_ADDRSTRLEN); switch (rtm->rtm_type) { case RTM_ADD: case RTM_GET: case RTM_CHANGE: /* The kernel notifies us about a new route in FIB created by us. Do we have a correspondent entry in our RIB? */ switch (ret) { case ZEBRA_RIB_NOTFOUND: zlog_debug ("%s: %s %s/%d: desync: RR isn't yet in RIB, while already in FIB", __func__, lookup (rtm_type_str, rtm->rtm_type), buf, p.prefixlen); break; case ZEBRA_RIB_FOUND_CONNECTED: case ZEBRA_RIB_FOUND_NOGATE: inet_ntop (AF_INET, &gate.sin.sin_addr, gate_buf, INET_ADDRSTRLEN); zlog_debug ("%s: %s %s/%d: desync: RR is in RIB, but gate differs (ours is %s)", __func__, lookup (rtm_type_str, rtm->rtm_type), buf, p.prefixlen, gate_buf); break; case ZEBRA_RIB_FOUND_EXACT: /* RIB RR == FIB RR */ zlog_debug ("%s: %s %s/%d: done Ok", __func__, lookup (rtm_type_str, rtm->rtm_type), buf, p.prefixlen); rib_lookup_and_dump (&p); return; break; } break; case RTM_DELETE: /* The kernel notifies us about a route deleted by us. Do we still have it in the RIB? Do we have anything instead? */ switch (ret) { case ZEBRA_RIB_FOUND_EXACT: zlog_debug ("%s: %s %s/%d: desync: RR is still in RIB, while already not in FIB", __func__, lookup (rtm_type_str, rtm->rtm_type), buf, p.prefixlen); rib_lookup_and_dump (&p); break; case ZEBRA_RIB_FOUND_CONNECTED: case ZEBRA_RIB_FOUND_NOGATE: zlog_debug ("%s: %s %s/%d: desync: RR is still in RIB, plus gate differs", __func__, lookup (rtm_type_str, rtm->rtm_type), buf, p.prefixlen); rib_lookup_and_dump (&p); break; case ZEBRA_RIB_NOTFOUND: /* RIB RR == FIB RR */ zlog_debug ("%s: %s %s/%d: done Ok", __func__, lookup (rtm_type_str, rtm->rtm_type), buf, p.prefixlen); rib_lookup_and_dump (&p); return; break; } break; default: zlog_debug ("%s: %s/%d: warning: loopback RTM of type %s received", __func__, buf, p.prefixlen, lookup (rtm_type_str, rtm->rtm_type)); } return; } /* Change, delete the old prefix, we have no further information * to specify the route really */ if (rtm->rtm_type == RTM_CHANGE) rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, NULL, 0, 0); if (rtm->rtm_type == RTM_GET || rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_CHANGE) rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, &gate.sin.sin_addr, NULL, 0, 0, 0, 0, rtm->rtm_protocol); else rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, &gate.sin.sin_addr, 0, 0); } #ifdef HAVE_IPV6 if (dest.sa.sa_family == AF_INET6) { /* One day we might have a debug section here like one in the * IPv4 case above. Just ignore own messages at the moment. */ if (rtm->rtm_type != RTM_GET && rtm->rtm_pid == pid) return; struct prefix_ipv6 p; unsigned int ifindex = 0; p.family = AF_INET6; p.prefix = dest.sin6.sin6_addr; if (flags & RTF_HOST) p.prefixlen = IPV6_MAX_PREFIXLEN; else p.prefixlen = ip6_masklen (mask.sin6.sin6_addr); #ifdef KAME if (IN6_IS_ADDR_LINKLOCAL (&gate.sin6.sin6_addr)) { ifindex = IN6_LINKLOCAL_IFINDEX (gate.sin6.sin6_addr); SET_IN6_LINKLOCAL_IFINDEX (gate.sin6.sin6_addr, 0); } #endif /* KAME */ /* CHANGE: delete the old prefix, we have no further information * to specify the route really */ if (rtm->rtm_type == RTM_CHANGE) rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, NULL, 0, 0); if (rtm->rtm_type == RTM_GET || rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_CHANGE) rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, &gate.sin6.sin6_addr, ifindex, 0, 0, 0); else rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, &gate.sin6.sin6_addr, ifindex, 0); } #endif /* HAVE_IPV6 */ }
/* Interface's address information get. */ int ifam_read (struct ifa_msghdr *ifam) { struct interface *ifp = NULL; union sockunion addr, mask, brd; char ifname[INTERFACE_NAMSIZ]; short ifnlen = 0; char isalias = 0; int flags = 0; ifname[0] = ifname[INTERFACE_NAMSIZ - 1] = '\0'; /* Allocate and read address information. */ ifam_read_mesg (ifam, &addr, &mask, &brd, ifname, &ifnlen); if ((ifp = if_lookup_by_index(ifam->ifam_index)) == NULL) { zlog_warn ("%s: no interface for ifname %s, index %d", __func__, ifname, ifam->ifam_index); return -1; } if (ifnlen && strncmp (ifp->name, ifname, INTERFACE_NAMSIZ)) isalias = 1; /* N.B. The info in ifa_msghdr does not tell us whether the RTA_BRD field contains a broadcast address or a peer address, so we are forced to rely upon the interface type. */ if (if_is_pointopoint(ifp)) SET_FLAG(flags, ZEBRA_IFA_PEER); #if 0 /* it might seem cute to grab the interface metric here, however * we're processing an address update message, and so some systems * (e.g. FBSD) dont bother to fill in ifam_metric. Disabled, but left * in deliberately, as comment. */ ifp->metric = ifam->ifam_metric; #endif /* Add connected address. */ switch (sockunion_family (&addr)) { case AF_INET: if (ifam->ifam_type == RTM_NEWADDR) connected_add_ipv4 (ifp, flags, &addr.sin.sin_addr, ip_masklen (mask.sin.sin_addr), &brd.sin.sin_addr, (isalias ? ifname : NULL)); else connected_delete_ipv4 (ifp, flags, &addr.sin.sin_addr, ip_masklen (mask.sin.sin_addr), &brd.sin.sin_addr); break; #ifdef HAVE_IPV6 case AF_INET6: /* Unset interface index from link-local address when IPv6 stack is KAME. */ if (IN6_IS_ADDR_LINKLOCAL (&addr.sin6.sin6_addr)) SET_IN6_LINKLOCAL_IFINDEX (addr.sin6.sin6_addr, 0); if (ifam->ifam_type == RTM_NEWADDR) connected_add_ipv6 (ifp, flags, &addr.sin6.sin6_addr, ip6_masklen (mask.sin6.sin6_addr), &brd.sin6.sin6_addr, (isalias ? ifname : NULL)); else connected_delete_ipv6 (ifp, &addr.sin6.sin6_addr, ip6_masklen (mask.sin6.sin6_addr), &brd.sin6.sin6_addr); break; #endif /* HAVE_IPV6 */ default: /* Unsupported family silently ignore... */ break; } /* Check interface flag for implicit up of the interface. */ if_refresh (ifp); #ifdef SUNOS_5 /* In addition to lacking IFANNOUNCE, on SUNOS IFF_UP is strange. * See comments for SUNOS_5 in interface.c::if_flags_mangle. * * Here we take care of case where the real IFF_UP was previously * unset (as kept in struct zebra_if.primary_state) and the mangled * IFF_UP (ie IFF_UP set || listcount(connected) has now transitioned * to unset due to the lost non-primary address having DELADDR'd. * * we must delete the interface, because in between here and next * event for this interface-name the administrator could unplumb * and replumb the interface. */ if (!if_is_up (ifp)) if_delete_update (ifp); #endif /* SUNOS_5 */ return 0; }
int bgp_nexthop_set (union sockunion *local, union sockunion *remote, struct bgp_nexthop *nexthop, struct peer *peer) { int ret = 0; struct interface *ifp = NULL; memset (nexthop, 0, sizeof (struct bgp_nexthop)); if (!local) return -1; if (!remote) return -1; if (local->sa.sa_family == AF_INET) { nexthop->v4 = local->sin.sin_addr; ifp = if_lookup_by_ipv4 (&local->sin.sin_addr); } #ifdef HAVE_IPV6 if (local->sa.sa_family == AF_INET6) { if (IN6_IS_ADDR_LINKLOCAL (&local->sin6.sin6_addr)) { if (peer->ifname) ifp = if_lookup_by_index (if_nametoindex (peer->ifname)); } else ifp = if_lookup_by_ipv6 (&local->sin6.sin6_addr); } #endif /* HAVE_IPV6 */ if (!ifp) return -1; nexthop->ifp = ifp; /* IPv4 connection. */ if (local->sa.sa_family == AF_INET) { #ifdef HAVE_IPV6 /* IPv6 nexthop*/ ret = if_get_ipv6_global (ifp, &nexthop->v6_global); /* There is no global nexthop. */ if (!ret) if_get_ipv6_local (ifp, &nexthop->v6_global); else if_get_ipv6_local (ifp, &nexthop->v6_local); #endif /* HAVE_IPV6 */ } #ifdef HAVE_IPV6 /* IPv6 connection. */ if (local->sa.sa_family == AF_INET6) { struct interface *direct = NULL; /* IPv4 nexthop. */ ret = if_get_ipv4_address(ifp, &nexthop->v4); if (!ret && peer->local_id.s_addr) nexthop->v4 = peer->local_id; /* Global address*/ if (! IN6_IS_ADDR_LINKLOCAL (&local->sin6.sin6_addr)) { memcpy (&nexthop->v6_global, &local->sin6.sin6_addr, IPV6_MAX_BYTELEN); /* If directory connected set link-local address. */ direct = if_lookup_by_ipv6 (&remote->sin6.sin6_addr); if (direct) if_get_ipv6_local (ifp, &nexthop->v6_local); } else /* Link-local address. */ { ret = if_get_ipv6_global (ifp, &nexthop->v6_global); /* If there is no global address. Set link-local address as global. I know this break RFC specification... */ if (!ret) memcpy (&nexthop->v6_global, &local->sin6.sin6_addr, IPV6_MAX_BYTELEN); else memcpy (&nexthop->v6_local, &local->sin6.sin6_addr, IPV6_MAX_BYTELEN); } } if (IN6_IS_ADDR_LINKLOCAL (&local->sin6.sin6_addr) || if_lookup_by_ipv6 (&remote->sin6.sin6_addr)) peer->shared_network = 1; else peer->shared_network = 0; /* KAME stack specific treatment. */ #ifdef KAME if (IN6_IS_ADDR_LINKLOCAL (&nexthop->v6_global) && IN6_LINKLOCAL_IFINDEX (nexthop->v6_global)) { SET_IN6_LINKLOCAL_IFINDEX (nexthop->v6_global, 0); } if (IN6_IS_ADDR_LINKLOCAL (&nexthop->v6_local) && IN6_LINKLOCAL_IFINDEX (nexthop->v6_local)) { SET_IN6_LINKLOCAL_IFINDEX (nexthop->v6_local, 0); } #endif /* KAME */ #endif /* HAVE_IPV6 */ return ret; }
/* Interface between zebra message and rtm message. */ static int kernel_rtm_ipv6_multipath(int cmd, struct prefix *p, struct rib *rib, int family) { struct sockaddr_in6 *mask; struct sockaddr_in6 sin_dest, sin_mask, sin_gate; struct nexthop *nexthop, *tnexthop; int recursing; int nexthop_num = 0; unsigned int ifindex = 0; int gate = 0; int error; memset(&sin_dest, 0, sizeof(struct sockaddr_in6)); sin_dest.sin6_family = AF_INET6; #ifdef SIN6_LEN sin_dest.sin6_len = sizeof(struct sockaddr_in6); #endif /* SIN6_LEN */ sin_dest.sin6_addr = p->u.prefix6; memset(&sin_mask, 0, sizeof(struct sockaddr_in6)); memset(&sin_gate, 0, sizeof(struct sockaddr_in6)); sin_gate.sin6_family = AF_INET6; #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sin_gate.sin6_len = sizeof(struct sockaddr_in6); #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ /* Make gateway. */ for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) continue; gate = 0; if ((cmd == RTM_ADD && CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) || (cmd == RTM_DELETE #if 0 && CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB) #endif )) { if (nexthop->type == NEXTHOP_TYPE_IPV6 || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) { sin_gate.sin6_addr = nexthop->gate.ipv6; gate = 1; } if (nexthop->type == NEXTHOP_TYPE_IFINDEX || nexthop->type == NEXTHOP_TYPE_IFNAME || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) ifindex = nexthop->ifindex; if (cmd == RTM_ADD) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); } /* Under kame set interface index to link local address. */ #ifdef KAME #define SET_IN6_LINKLOCAL_IFINDEX(a, i) \ do { \ (a).s6_addr[2] = ((i) >> 8) & 0xff; \ (a).s6_addr[3] = (i) & 0xff; \ } while (0) if (gate && IN6_IS_ADDR_LINKLOCAL(&sin_gate.sin6_addr)) SET_IN6_LINKLOCAL_IFINDEX(sin_gate.sin6_addr, ifindex); #endif /* KAME */ if (gate && p->prefixlen == 128) mask = NULL; else { masklen2ip6(p->prefixlen, &sin_mask.sin6_addr); sin_mask.sin6_family = AF_INET6; #ifdef SIN6_LEN sin_mask.sin6_len = sin6_masklen(sin_mask.sin6_addr); #endif /* SIN6_LEN */ mask = &sin_mask; } error = rtm_write(cmd, (union sockunion *)&sin_dest, (union sockunion *)mask, gate ? (union sockunion *)&sin_gate : NULL, ifindex, rib->flags, rib->metric); #if 0 if (error) { zlog_info ("kernel_rtm_ipv6_multipath(): nexthop %d add error=%d.", nexthop_num, error); } #endif nexthop_num++; } /* If there is no useful nexthop then return. */ if (nexthop_num == 0) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug ("kernel_rtm_ipv6_multipath(): No useful nexthop."); return 0; } return 0; /*XXX*/}
/* sockunion_connect returns -1 : error occured 0 : connect success 1 : connect is in progress */ enum connect_result sockunion_connect (int fd, union sockunion *peersu, unsigned short port, unsigned int ifindex) { int ret; int val; union sockunion su; int ret2=0; memcpy (&su, peersu, sizeof (union sockunion)); switch (su.sa.sa_family) { case AF_INET: su.sin.sin_port = port; break; #ifdef HAVE_IPV6 case AF_INET6: su.sin6.sin6_port = port; #ifdef KAME if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) && ifindex) { #ifdef HAVE_SIN6_SCOPE_ID /* su.sin6.sin6_scope_id = ifindex; */ #ifdef MUSICA su.sin6.sin6_scope_id = ifindex; #endif #endif /* HAVE_SIN6_SCOPE_ID */ #ifndef MUSICA SET_IN6_LINKLOCAL_IFINDEX (su.sin6.sin6_addr, ifindex); #endif } #endif /* KAME */ break; #endif /* HAVE_IPV6 */ } /* Make socket non-block. */ val = fcntl (fd, F_GETFL, 0); /*CID 10316 (#1 of 3): Unchecked return value from library (CHECKED_RETURN) 3. check_return: Calling function "fcntl(fd, 4, val | 0x80)" without checking return value. This library function may fail and return an error code. Add check return value.*/ /*fcntl (fd, F_SETFL, val|O_NONBLOCK);*/ ret = fcntl (fd, F_SETFL, val|O_NONBLOCK); if(ret < 0) zlog_warn("%s: line %d, fcntl failed : %s .\n",__func__,__LINE__,safe_strerror(errno)); /* Call connect function. */ ret = connect (fd, (struct sockaddr *) &su, sockunion_sizeof (&su)); /* Immediate success */ if (ret == 0) { /*CID 10316 (#2 of 3): Unchecked return value from library (CHECKED_RETURN) 4. check_return: Calling function "fcntl(fd, 4, val)" without checking return value. This library function may fail and return an error code. Change , add check return value.*/ /*fcntl (fd, F_SETFL, val);*/ ret2 = fcntl (fd, F_SETFL, val); if(ret < 0) zlog_warn("%s: line %d, fcntl failed : %s .\n",__func__,__LINE__,safe_strerror(errno)); return connect_success; } /* If connect is in progress then return 1 else it's real error. */ if (ret < 0) { if (errno != EINPROGRESS) { char *log_str = sockunion_log (&su); zlog_info ("can't connect to %s fd %d : %s", log_str, fd, safe_strerror (errno)); free(log_str); return connect_error; } } /*CID 10316 (#3 of 3): Unchecked return value from library (CHECKED_RETURN) 6. check_return: Calling function "fcntl(fd, 4, val)" without checking return value. This library function may fail and return an error code. Add check return value.*/ /*fcntl (fd, F_SETFL, val);*/ ret2 = fcntl (fd, F_SETFL, val); if(ret < 0) zlog_warn("%s: line %d, fcntl failed : %s .\n",__func__,__LINE__,safe_strerror(errno)); return connect_in_progress; }
void rtm_read (struct rt_msghdr *rtm) { int flags; u_char zebra_flags; union sockunion dest, mask, gate; char ifname[INTERFACE_NAMSIZ + 1]; short ifnlen = 0; zebra_flags = 0; /* Discard self send message. */ if (rtm->rtm_type != RTM_GET && (rtm->rtm_pid == pid || rtm->rtm_pid == old_pid)) return; /* Read destination and netmask and gateway from rtm message structure. */ flags = rtm_read_mesg (rtm, &dest, &mask, &gate, ifname, &ifnlen); #ifdef RTF_CLONED /*bsdi, netbsd 1.6*/ if (flags & RTF_CLONED) return; #endif #ifdef RTF_WASCLONED /*freebsd*/ if (flags & RTF_WASCLONED) return; #endif if ((rtm->rtm_type == RTM_ADD) && ! (flags & RTF_UP)) return; /* This is connected route. */ if (! (flags & RTF_GATEWAY)) return; if (flags & RTF_PROTO1) SET_FLAG (zebra_flags, ZEBRA_FLAG_SELFROUTE); /* This is persistent route. */ if (flags & RTF_STATIC) SET_FLAG (zebra_flags, ZEBRA_FLAG_STATIC); /* This is a reject or blackhole route */ if (flags & RTF_REJECT) SET_FLAG (zebra_flags, ZEBRA_FLAG_REJECT); if (flags & RTF_BLACKHOLE) SET_FLAG (zebra_flags, ZEBRA_FLAG_BLACKHOLE); if (dest.sa.sa_family == AF_INET) { struct prefix_ipv4 p; p.family = AF_INET; p.prefix = dest.sin.sin_addr; if (flags & RTF_HOST) p.prefixlen = IPV4_MAX_PREFIXLEN; else p.prefixlen = ip_masklen (mask.sin.sin_addr); /* Change, delete the old prefix, we have no further information * to specify the route really */ if (rtm->rtm_type == RTM_CHANGE) rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, NULL, 0, 0); if (rtm->rtm_type == RTM_GET || rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_CHANGE) rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, &gate.sin.sin_addr, 0, 0, 0, 0); else rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, &gate.sin.sin_addr, 0, 0); } #ifdef HAVE_IPV6 if (dest.sa.sa_family == AF_INET6) { struct prefix_ipv6 p; unsigned int ifindex = 0; p.family = AF_INET6; p.prefix = dest.sin6.sin6_addr; if (flags & RTF_HOST) p.prefixlen = IPV6_MAX_PREFIXLEN; else p.prefixlen = ip6_masklen (mask.sin6.sin6_addr); #ifdef KAME if (IN6_IS_ADDR_LINKLOCAL (&gate.sin6.sin6_addr)) { ifindex = IN6_LINKLOCAL_IFINDEX (gate.sin6.sin6_addr); SET_IN6_LINKLOCAL_IFINDEX (gate.sin6.sin6_addr, 0); } #endif /* KAME */ /* CHANGE: delete the old prefix, we have no further information * to specify the route really */ if (rtm->rtm_type == RTM_CHANGE) rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, NULL, 0, 0); if (rtm->rtm_type == RTM_GET || rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_CHANGE) rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, &gate.sin6.sin6_addr, ifindex, 0, 0, 0); else rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, &gate.sin6.sin6_addr, ifindex, 0); } #endif /* HAVE_IPV6 */ }
static int parse_kernel_route(const struct rt_msghdr *rtm, struct kernel_route *route) { struct sockaddr *sa; char *rta = (char*)rtm + sizeof(struct rt_msghdr); uint32_t excluded_flags = 0; if(ifindex_lo < 0) { ifindex_lo = if_nametoindex("lo0"); if(ifindex_lo <= 0) return -1; } memset(route, 0, sizeof(*route)); route->metric = 0; route->ifindex = rtm->rtm_index; #if defined(RTF_IFSCOPE) /* Filter out kernel route on OS X */ excluded_flags |= RTF_IFSCOPE; #endif #if defined(RTF_MULTICAST) /* Filter out multicast route on others BSD */ excluded_flags |= RTF_MULTICAST; #endif /* Filter out our own route */ excluded_flags |= RTF_PROTO2; if((rtm->rtm_flags & excluded_flags) != 0) return -1; /* Prefix */ if(!(rtm->rtm_addrs & RTA_DST)) return -1; sa = (struct sockaddr *)rta; rta += ROUNDUP(sa->sa_len); if(sa->sa_family == AF_INET6) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; memcpy(route->prefix, &sin6->sin6_addr, 16); if(IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&sin6->sin6_addr)) return -1; } else if(sa->sa_family == AF_INET) { struct sockaddr_in *sin = (struct sockaddr_in *)sa; #if defined(IN_LINKLOCAL) if(IN_LINKLOCAL(ntohl(sin->sin_addr.s_addr))) return -1; #endif if(IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) return -1; v4tov6(route->prefix, (unsigned char *)&sin->sin_addr); } else { return -1; } /* Gateway */ if(!(rtm->rtm_addrs & RTA_GATEWAY)) return -1; sa = (struct sockaddr *)rta; rta += ROUNDUP(sa->sa_len); if(sa->sa_family == AF_INET6) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; memcpy(route->gw, &sin6->sin6_addr, 16); if(IN6_IS_ADDR_LINKLOCAL (&sin6->sin6_addr)) { route->ifindex = IN6_LINKLOCAL_IFINDEX(sin6->sin6_addr); SET_IN6_LINKLOCAL_IFINDEX(sin6->sin6_addr, 0); } } else if(sa->sa_family == AF_INET) { struct sockaddr_in *sin = (struct sockaddr_in *)sa; v4tov6(route->gw, (unsigned char *)&sin->sin_addr); } if((int)route->ifindex == ifindex_lo) return -1; /* Netmask */ if((rtm->rtm_addrs & RTA_NETMASK) != 0) { sa = (struct sockaddr *)rta; rta += ROUNDUP(sa->sa_len); if(!v4mapped(route->prefix)) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; route->plen = mask2len((unsigned char*)&sin6->sin6_addr, 16); } else { struct sockaddr_in *sin = (struct sockaddr_in *)sa; route->plen = mask2len((unsigned char*)&sin->sin_addr, 4); } } if(v4mapped(route->prefix)) route->plen += 96; if(rtm->rtm_flags & RTF_HOST) route->plen = 128; return 0; }
int kernel_route(int operation, const unsigned char *dest, unsigned short plen, const unsigned char *gate, int ifindex, unsigned int metric, const unsigned char *newgate, int newifindex, unsigned int newmetric) { unsigned char msg[512]; struct rt_msghdr *rtm; struct sockaddr_in6 *sin6; struct sockaddr_in *sin; int rc, len, ipv4; char local6[1][1][16] = IN6ADDR_LOOPBACK_INIT; char local4[1][1][16] = {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x01 }}}; /* Check that the protocol family is consistent. */ if(plen >= 96 && v4mapped(dest)) { if(!v4mapped(gate)) { errno = EINVAL; return -1; } ipv4 = 1; } else { if(v4mapped(gate)) { errno = EINVAL; return -1; } ipv4 = 0; } if(operation == ROUTE_MODIFY && newmetric == metric && memcmp(newgate, gate, 16) == 0 && newifindex == ifindex) return 0; if(operation == ROUTE_MODIFY) { metric = newmetric; gate = newgate; ifindex = newifindex; } kdebugf("kernel_route: %s %s/%d metric %d dev %d nexthop %s\n", operation == ROUTE_ADD ? "add" : operation == ROUTE_FLUSH ? "flush" : "change", format_address(dest), plen, metric, ifindex, format_address(gate)); if(kernel_socket < 0) kernel_setup_socket(1); memset(&msg, 0, sizeof(msg)); rtm = (struct rt_msghdr *)msg; rtm->rtm_version = RTM_VERSION; switch(operation) { case ROUTE_FLUSH: rtm->rtm_type = RTM_DELETE; break; case ROUTE_ADD: rtm->rtm_type = RTM_ADD; break; case ROUTE_MODIFY: rtm->rtm_type = RTM_CHANGE; break; default: return -1; }; rtm->rtm_index = ifindex; rtm->rtm_flags = RTF_UP | RTF_PROTO2; if(plen == 128) rtm->rtm_flags |= RTF_HOST; /* if(memcmp(nexthop->id, dest, 16) == 0) { */ /* rtm -> rtm_flags |= RTF_LLINFO; */ /* rtm -> rtm_flags |= RTF_CLONING; */ /* } else { */ rtm->rtm_flags |= RTF_GATEWAY; /* } */ if(metric == KERNEL_INFINITY) { rtm->rtm_flags |= RTF_BLACKHOLE; if(ifindex_lo < 0) { ifindex_lo = if_nametoindex("lo0"); if(ifindex_lo <= 0) return -1; } rtm->rtm_index = ifindex_lo; } rtm->rtm_seq = ++seq; rtm->rtm_addrs = RTA_DST | RTA_GATEWAY; if(!(operation == ROUTE_MODIFY && plen == 128)) { rtm->rtm_addrs |= RTA_NETMASK; } #define push_sockaddr_in(ptr, offset) \ do { (ptr) = (struct sockaddr_in *)((char *)(ptr) + (offset)); \ (ptr)->sin_len = sizeof(struct sockaddr_in); \ (ptr)->sin_family = AF_INET; } while (0) #define get_sin_addr(dst,src) \ do { memcpy((dst), (src) + 12, 4); } while (0) #define push_sockaddr_in6(ptr, offset) \ do { (ptr) = (struct sockaddr_in6 *)((char *)(ptr) + (offset)); \ (ptr)->sin6_len = sizeof(struct sockaddr_in6); \ (ptr)->sin6_family = AF_INET6; } while (0) #define get_sin6_addr(dst,src) \ do { memcpy((dst), (src), 16); } while (0) /* KAME ipv6 stack does not support IPv4 mapped IPv6, so we have to * duplicate the codepath */ if(ipv4) { sin = (struct sockaddr_in *)msg; /* destination */ push_sockaddr_in(sin, sizeof(*rtm)); get_sin_addr(&(sin->sin_addr), dest); /* gateway */ push_sockaddr_in(sin, ROUNDUP(sin->sin_len)); if (metric == KERNEL_INFINITY) get_sin_addr(&(sin->sin_addr),**local4); else get_sin_addr(&(sin->sin_addr),gate); /* netmask */ if((rtm->rtm_addrs | RTA_NETMASK) != 0) { struct in6_addr tmp_sin6_addr; push_sockaddr_in(sin, ROUNDUP(sin->sin_len)); plen2mask(plen, &tmp_sin6_addr); get_sin_addr(&(sin->sin_addr), (char *)&tmp_sin6_addr); } len = (char *)sin + ROUNDUP(sin->sin_len) - (char *)msg; } else { sin6 = (struct sockaddr_in6 *)msg; /* destination */ push_sockaddr_in6(sin6, sizeof(*rtm)); get_sin6_addr(&(sin6->sin6_addr), dest); /* gateway */ push_sockaddr_in6(sin6, ROUNDUP(sin6->sin6_len)); if (metric == KERNEL_INFINITY) get_sin6_addr(&(sin6->sin6_addr),**local6); else get_sin6_addr(&(sin6->sin6_addr),gate); if(IN6_IS_ADDR_LINKLOCAL (&sin6->sin6_addr)) SET_IN6_LINKLOCAL_IFINDEX (sin6->sin6_addr, ifindex); /* netmask */ if((rtm->rtm_addrs | RTA_NETMASK) != 0) { push_sockaddr_in6(sin6, ROUNDUP(sin6->sin6_len)); plen2mask(plen, &sin6->sin6_addr); } len = (char *)sin6 + ROUNDUP(sin6->sin6_len) - (char *)msg; } rtm->rtm_msglen = len; rc = write(kernel_socket, msg, rtm->rtm_msglen); if (rc < rtm->rtm_msglen) return -1; return 1; }