diff options
Diffstat (limited to 'ospf6d/ospf6_interface.c')
| -rw-r--r-- | ospf6d/ospf6_interface.c | 384 |
1 files changed, 291 insertions, 93 deletions
diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index c2f9c3362e..a169b9c60e 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -36,6 +36,7 @@ #include "ospf6_message.h" #include "ospf6_route.h" #include "ospf6_area.h" +#include "ospf6_abr.h" #include "ospf6_interface.h" #include "ospf6_neighbor.h" #include "ospf6_intra.h" @@ -109,7 +110,7 @@ static uint8_t ospf6_default_iftype(struct interface *ifp) { if (if_is_pointopoint(ifp)) return OSPF_IFTYPE_POINTOPOINT; - else if (if_is_loopback(ifp)) + else if (if_is_loopback_or_vrf(ifp)) return OSPF_IFTYPE_LOOPBACK; else return OSPF_IFTYPE_BROADCAST; @@ -184,6 +185,8 @@ struct ospf6_interface *ospf6_interface_create(struct interface *ifp) oi = XCALLOC(MTYPE_OSPF6_IF, sizeof(struct ospf6_interface)); + oi->obuf = ospf6_fifo_new(); + oi->area = (struct ospf6_area *)NULL; oi->neighbor_list = list_new(); oi->neighbor_list->cmp = ospf6_neighbor_cmp; @@ -242,6 +245,8 @@ void ospf6_interface_delete(struct ospf6_interface *oi) QOBJ_UNREG(oi); + ospf6_fifo_free(oi->obuf); + for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) ospf6_neighbor_delete(on); @@ -330,31 +335,6 @@ ospf6_interface_get_linklocal_address(struct interface *ifp) return l; } -void ospf6_interface_if_add(struct interface *ifp) -{ - struct ospf6_interface *oi; - unsigned int iobuflen; - - oi = (struct ospf6_interface *)ifp->info; - if (oi == NULL) - return; - - /* Try to adjust I/O buffer size with IfMtu */ - if (oi->ifmtu == 0) - oi->ifmtu = ifp->mtu6; - iobuflen = ospf6_iobuf_size(ifp->mtu6); - if (oi->ifmtu > iobuflen) { - if (IS_OSPF6_DEBUG_INTERFACE) - zlog_debug( - "Interface %s: IfMtu is adjusted to I/O buffer size: %d.", - ifp->name, iobuflen); - oi->ifmtu = iobuflen; - } - - /* interface start */ - ospf6_interface_state_update(oi->interface); -} - void ospf6_interface_state_update(struct interface *ifp) { struct ospf6_interface *oi; @@ -390,7 +370,7 @@ void ospf6_interface_state_update(struct interface *ifp) if (if_is_operative(ifp) && (ospf6_interface_get_linklocal_address(oi->interface) - || if_is_loopback(oi->interface))) + || if_is_loopback_or_vrf(oi->interface))) thread_execute(master, interface_up, oi, 0); else thread_execute(master, interface_down, oi, 0); @@ -405,7 +385,6 @@ void ospf6_interface_connected_route_update(struct interface *ifp) struct connected *c; struct listnode *node, *nnode; struct in6_addr nh_addr; - int count = 0, max_addr_count; oi = (struct ospf6_interface *)ifp->info; if (oi == NULL) @@ -424,22 +403,10 @@ void ospf6_interface_connected_route_update(struct interface *ifp) /* update "route to advertise" interface route table */ ospf6_route_remove_all(oi->route_connected); - if (oi->ifmtu >= OSPF6_JUMBO_MTU) - max_addr_count = OSPF6_MAX_IF_ADDRS_JUMBO; - else - max_addr_count = OSPF6_MAX_IF_ADDRS; - for (ALL_LIST_ELEMENTS(oi->interface->connected, node, nnode, c)) { if (c->address->family != AF_INET6) continue; - /* number of interface addresses supported is based on MTU - * size of OSPFv3 packet - */ - count++; - if (count >= max_addr_count) - break; - CONTINUE_IF_ADDRESS_LINKLOCAL(IS_OSPF6_DEBUG_INTERFACE, c->address); CONTINUE_IF_ADDRESS_UNSPECIFIED(IS_OSPF6_DEBUG_INTERFACE, @@ -468,7 +435,7 @@ void ospf6_interface_connected_route_update(struct interface *ifp) } } - route = ospf6_route_create(); + route = ospf6_route_create(oi->area->ospf6); memcpy(&route->prefix, c->address, sizeof(struct prefix)); apply_mask(&route->prefix); route->type = OSPF6_DEST_TYPE_NETWORK; @@ -499,6 +466,9 @@ static int ospf6_interface_state_change(uint8_t next_state, if (prev_state == next_state) return -1; + if (!oi->area) + return -1; + /* log */ if (IS_OSPF6_DEBUG_INTERFACE) { zlog_debug("Interface state change %s: %s -> %s", @@ -507,7 +477,8 @@ static int ospf6_interface_state_change(uint8_t next_state, ospf6_interface_state_str[next_state]); } oi->state_change++; - ospf6 = ospf6_lookup_by_vrf_id(oi->interface->vrf_id); + + ospf6 = oi->area->ospf6; if ((prev_state == OSPF6_INTERFACE_DR || prev_state == OSPF6_INTERFACE_BDR) @@ -696,6 +667,43 @@ static uint8_t dr_election(struct ospf6_interface *oi) return next_state; } +#ifdef __FreeBSD__ + +#include <ifaddrs.h> + +static bool ifmaddr_check(ifindex_t ifindex, struct in6_addr *addr) +{ + struct ifmaddrs *ifmap, *ifma; + struct sockaddr_dl *sdl; + struct sockaddr_in6 *sin6; + bool found = false; + + if (getifmaddrs(&ifmap) != 0) + return false; + + for (ifma = ifmap; ifma; ifma = ifma->ifma_next) { + if (ifma->ifma_name == NULL || ifma->ifma_addr == NULL) + continue; + if (ifma->ifma_name->sa_family != AF_LINK) + continue; + if (ifma->ifma_addr->sa_family != AF_INET6) + continue; + sdl = (struct sockaddr_dl *)ifma->ifma_name; + sin6 = (struct sockaddr_in6 *)ifma->ifma_addr; + if (sdl->sdl_index == ifindex + && memcmp(&sin6->sin6_addr, addr, IPV6_MAX_BYTELEN) == 0) { + found = true; + break; + } + } + + if (ifmap) + freeifmaddrs(ifmap); + + return found; +} + +#endif /* __FreeBSD__ */ /* Interface State Machine */ int interface_up(struct thread *thread) @@ -709,11 +717,7 @@ int interface_up(struct thread *thread) if (!oi->type_cfg) oi->type = ospf6_default_iftype(oi->interface); - /* - * Remove old pointer. If this thread wasn't a timer this - * operation won't make a difference, because it is already NULL. - */ - oi->thread_sso = NULL; + thread_cancel(&oi->thread_sso); if (IS_OSPF6_DEBUG_INTERFACE) zlog_debug("Interface Event %s: [InterfaceUp]", @@ -721,20 +725,17 @@ int interface_up(struct thread *thread) /* check physical interface is up */ if (!if_is_operative(oi->interface)) { - if (IS_OSPF6_DEBUG_INTERFACE) - zlog_debug( - "Interface %s is down, can't execute [InterfaceUp]", - oi->interface->name); + zlog_warn("Interface %s is down, can't execute [InterfaceUp]", + oi->interface->name); return 0; } /* check interface has a link-local address */ if (!(ospf6_interface_get_linklocal_address(oi->interface) - || if_is_loopback(oi->interface))) { - if (IS_OSPF6_DEBUG_INTERFACE) - zlog_debug( - "Interface %s has no link local address, can't execute [InterfaceUp]", - oi->interface->name); + || if_is_loopback_or_vrf(oi->interface))) { + zlog_warn( + "Interface %s has no link local address, can't execute [InterfaceUp]", + oi->interface->name); return 0; } @@ -751,31 +752,33 @@ int interface_up(struct thread *thread) /* If no area assigned, return */ if (oi->area == NULL) { - zlog_debug( - "%s: Not scheduleing Hello for %s as there is no area assigned yet", + zlog_warn( + "%s: Not scheduling Hello for %s as there is no area assigned yet", __func__, oi->interface->name); return 0; } #ifdef __FreeBSD__ /* - * XXX: Schedule IPv6 group join for later, otherwise we might - * lose the multicast group registration caused by IPv6 group - * leave race. + * There's a delay in FreeBSD between issuing a command to leave a + * multicast group and an actual leave. If we execute "no router ospf6" + * and "router ospf6" fast enough, we can end up in a situation when OS + * performs the leave later than it performs the join and the interface + * remains without a multicast group. We have to do the join only after + * the interface actually left the group. */ - if (oi->sso_try_cnt == 0) { - oi->sso_try_cnt++; - zlog_info("Scheduling %s for sso", oi->interface->name); + if (ifmaddr_check(oi->interface->ifindex, &allspfrouters6)) { + zlog_info( + "Interface %s is still in all routers group, rescheduling for SSO", + oi->interface->name); thread_add_timer(master, interface_up, oi, OSPF6_INTERFACE_SSO_RETRY_INT, &oi->thread_sso); return 0; } #endif /* __FreeBSD__ */ - if (oi->area->ospf6) - ospf6 = oi->area->ospf6; - else - ospf6 = ospf6_lookup_by_vrf_id(oi->interface->vrf_id); + + ospf6 = oi->area->ospf6; /* Join AllSPFRouters */ if (ospf6_sso(oi->interface->ifindex, &allspfrouters6, IPV6_JOIN_GROUP, @@ -798,14 +801,16 @@ int interface_up(struct thread *thread) /* Schedule Hello */ if (!CHECK_FLAG(oi->flag, OSPF6_INTERFACE_PASSIVE) - && !if_is_loopback(oi->interface)) { + && !if_is_loopback_or_vrf(oi->interface)) { oi->thread_send_hello = NULL; thread_add_event(master, ospf6_hello_send, oi, 0, &oi->thread_send_hello); } /* decide next interface state */ - if (oi->type == OSPF_IFTYPE_POINTOPOINT) { + if (oi->type == OSPF_IFTYPE_LOOPBACK) { + ospf6_interface_state_change(OSPF6_INTERFACE_LOOPBACK, oi); + } else if (oi->type == OSPF_IFTYPE_POINTOPOINT) { ospf6_interface_state_change(OSPF6_INTERFACE_POINTTOPOINT, oi); } else if (oi->priority == 0) ospf6_interface_state_change(OSPF6_INTERFACE_DROTHER, oi); @@ -890,13 +895,6 @@ int interface_down(struct thread *thread) /* Stop trying to set socket options. */ THREAD_OFF(oi->thread_sso); - ospf6 = ospf6_lookup_by_vrf_id(oi->interface->vrf_id); - /* Leave AllSPFRouters */ - if (oi->state > OSPF6_INTERFACE_DOWN) - ospf6_sso(oi->interface->ifindex, &allspfrouters6, - IPV6_LEAVE_GROUP, ospf6->fd); - - ospf6_interface_state_change(OSPF6_INTERFACE_DOWN, oi); for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) ospf6_neighbor_delete(on); @@ -907,6 +905,27 @@ int interface_down(struct thread *thread) * DR election, as it is no longer valid. */ oi->drouter = oi->prev_drouter = htonl(0); oi->bdrouter = oi->prev_bdrouter = htonl(0); + + if (oi->area == NULL) + return 0; + + ospf6 = oi->area->ospf6; + /* Leave AllSPFRouters */ + if (oi->state > OSPF6_INTERFACE_DOWN) + ospf6_sso(oi->interface->ifindex, &allspfrouters6, + IPV6_LEAVE_GROUP, ospf6->fd); + + /* deal with write fifo */ + ospf6_fifo_flush(oi->obuf); + if (oi->on_write_q) { + listnode_delete(ospf6->oi_write_q, oi); + if (list_isempty(ospf6->oi_write_q)) + thread_cancel(&ospf6->t_write); + oi->on_write_q = 0; + } + + ospf6_interface_state_change(OSPF6_INTERFACE_DOWN, oi); + return 0; } @@ -1190,6 +1209,26 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp, return 0; } +/* Find the global address to be used as a forwarding address in NSSA LSA.*/ +struct in6_addr *ospf6_interface_get_global_address(struct interface *ifp) +{ + struct listnode *n; + struct connected *c; + struct in6_addr *l = (struct in6_addr *)NULL; + + /* for each connected address */ + for (ALL_LIST_ELEMENTS_RO(ifp->connected, n, c)) { + /* if family not AF_INET6, ignore */ + if (c->address->family != AF_INET6) + continue; + + if (!IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6)) + l = &c->address->u.prefix6; + } + return l; +} + + static int show_ospf6_interface_common(struct vty *vty, vrf_id_t vrf_id, int argc, struct cmd_token **argv, int idx_ifname, int intf_idx, @@ -1611,7 +1650,128 @@ DEFUN(show_ipv6_ospf6_interface_prefix, show_ipv6_ospf6_interface_prefix_cmd, return CMD_SUCCESS; } +void ospf6_interface_start(struct ospf6_interface *oi) +{ + struct ospf6 *ospf6; + struct ospf6_area *oa; + + if (oi->area_id_format == OSPF6_AREA_FMT_UNSET) + return; + + if (oi->area) + return; + + ospf6 = ospf6_lookup_by_vrf_id(oi->interface->vrf_id); + if (!ospf6) + return; + + oa = ospf6_area_lookup(oi->area_id, ospf6); + if (oa == NULL) + oa = ospf6_area_create(oi->area_id, ospf6, oi->area_id_format); + + /* attach interface to area */ + listnode_add(oa->if_list, oi); + oi->area = oa; + + SET_FLAG(oa->flag, OSPF6_AREA_ENABLE); + + /* start up */ + ospf6_interface_enable(oi); + + /* If the router is ABR, originate summary routes */ + if (ospf6_check_and_set_router_abr(ospf6)) + ospf6_abr_enable_area(oa); +} + +void ospf6_interface_stop(struct ospf6_interface *oi) +{ + struct ospf6_area *oa; + + oa = oi->area; + if (!oa) + return; + + ospf6_interface_disable(oi); + + listnode_delete(oa->if_list, oi); + oi->area = NULL; + + if (oa->if_list->count == 0) { + UNSET_FLAG(oa->flag, OSPF6_AREA_ENABLE); + ospf6_abr_disable_area(oa); + } +} + /* interface variable set command */ +DEFUN (ipv6_ospf6_area, + ipv6_ospf6_area_cmd, + "ipv6 ospf6 area <A.B.C.D|(0-4294967295)>", + IP6_STR + OSPF6_STR + "Specify the OSPF6 area ID\n" + "OSPF6 area ID in IPv4 address notation\n" + "OSPF6 area ID in decimal notation\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; + int idx_ipv4 = 3; + uint32_t area_id; + int format; + + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + assert(oi); + + if (oi->area) { + vty_out(vty, "%s already attached to Area %s\n", + oi->interface->name, oi->area->name); + return CMD_SUCCESS; + } + + if (str2area_id(argv[idx_ipv4]->arg, &area_id, &format)) { + vty_out(vty, "Malformed Area-ID: %s\n", argv[idx_ipv4]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + + oi->area_id = area_id; + oi->area_id_format = format; + + ospf6_interface_start(oi); + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_ospf6_area, + no_ipv6_ospf6_area_cmd, + "no ipv6 ospf6 area [<A.B.C.D|(0-4294967295)>]", + NO_STR + IP6_STR + OSPF6_STR + "Specify the OSPF6 area ID\n" + "OSPF6 area ID in IPv4 address notation\n" + "OSPF6 area ID in decimal notation\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; + + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + assert(oi); + + ospf6_interface_stop(oi); + + oi->area_id = 0; + oi->area_id_format = OSPF6_AREA_FMT_UNSET; + + return CMD_SUCCESS; +} + DEFUN (ipv6_ospf6_ifmtu, ipv6_ospf6_ifmtu_cmd, "ipv6 ospf6 ifmtu (1-65535)", @@ -1833,6 +1993,38 @@ DEFUN (no_auto_cost_reference_bandwidth, } +DEFUN (ospf6_write_multiplier, + ospf6_write_multiplier_cmd, + "write-multiplier (1-100)", + "Write multiplier\n" + "Maximum number of interface serviced per write\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, o); + uint32_t write_oi_count; + + write_oi_count = strtol(argv[1]->arg, NULL, 10); + if (write_oi_count < 1 || write_oi_count > 100) { + vty_out(vty, "write-multiplier value is invalid\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + o->write_oi_count = write_oi_count; + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_write_multiplier, + no_ospf6_write_multiplier_cmd, + "no write-multiplier (1-100)", + NO_STR + "Write multiplier\n" + "Maximum number of interface serviced per write\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, o); + + o->write_oi_count = OSPF6_WRITE_INTERFACE_COUNT_DEFAULT; + return CMD_SUCCESS; +} + DEFUN (ipv6_ospf6_hellointerval, ipv6_ospf6_hellointerval_cmd, "ipv6 ospf6 hello-interval (1-65535)", @@ -2099,7 +2291,7 @@ DEFUN (no_ipv6_ospf6_passive, THREAD_OFF(oi->thread_sso); /* don't send hellos over loopback interface */ - if (!if_is_loopback(oi->interface)) + if (!if_is_loopback_or_vrf(oi->interface)) thread_add_event(master, ospf6_hello_send, oi, 0, &oi->thread_send_hello); @@ -2307,6 +2499,7 @@ static int config_write_ospf6_interface(struct vty *vty, struct vrf *vrf) { struct ospf6_interface *oi; struct interface *ifp; + char buf[INET_ADDRSTRLEN]; FOR_ALL_INTERFACES (vrf, ifp) { oi = (struct ospf6_interface *)ifp->info; @@ -2321,6 +2514,11 @@ static int config_write_ospf6_interface(struct vty *vty, struct vrf *vrf) if (ifp->desc) vty_out(vty, " description %s\n", ifp->desc); + if (oi->area_id_format != OSPF6_AREA_FMT_UNSET) { + area_id2str(buf, sizeof(buf), oi->area_id, + oi->area_id_format); + vty_out(vty, " ipv6 ospf6 area %s\n", buf); + } if (oi->c_ifmtu) vty_out(vty, " ipv6 ospf6 ifmtu %d\n", oi->c_ifmtu); @@ -2386,21 +2584,14 @@ static int config_write_interface(struct vty *vty) return write; } -static int config_write_ospf6_interface(struct vty *vty, struct vrf *vrf); -static struct cmd_node interface_node = { - .name = "interface", - .node = INTERFACE_NODE, - .parent_node = CONFIG_NODE, - .prompt = "%s(config-if)# ", - .config_write = config_write_interface, -}; - static int ospf6_ifp_create(struct interface *ifp) { if (IS_OSPF6_DEBUG_ZEBRA(RECV)) zlog_debug("Zebra Interface add: %s index %d mtu %d", ifp->name, ifp->ifindex, ifp->mtu6); - ospf6_interface_if_add(ifp); + + if (ifp->info) + ospf6_interface_start(ifp->info); return 0; } @@ -2441,14 +2632,16 @@ static int ospf6_ifp_destroy(struct interface *ifp) zlog_debug("Zebra Interface delete: %s index %d mtu %d", ifp->name, ifp->ifindex, ifp->mtu6); + if (ifp->info) + ospf6_interface_stop(ifp->info); + return 0; } void ospf6_interface_init(void) { /* Install interface node. */ - install_node(&interface_node); - if_cmd_init(); + if_cmd_init(config_write_interface); if_zapi_callbacks(ospf6_ifp_create, ospf6_ifp_up, ospf6_ifp_down, ospf6_ifp_destroy); @@ -2458,6 +2651,8 @@ void ospf6_interface_init(void) &show_ipv6_ospf6_interface_ifname_prefix_cmd); install_element(VIEW_NODE, &show_ipv6_ospf6_interface_traffic_cmd); + install_element(INTERFACE_NODE, &ipv6_ospf6_area_cmd); + install_element(INTERFACE_NODE, &no_ipv6_ospf6_area_cmd); install_element(INTERFACE_NODE, &ipv6_ospf6_cost_cmd); install_element(INTERFACE_NODE, &no_ipv6_ospf6_cost_cmd); install_element(INTERFACE_NODE, &ipv6_ospf6_ifmtu_cmd); @@ -2492,10 +2687,13 @@ void ospf6_interface_init(void) /* reference bandwidth commands */ install_element(OSPF6_NODE, &auto_cost_reference_bandwidth_cmd); install_element(OSPF6_NODE, &no_auto_cost_reference_bandwidth_cmd); + /* write-multiplier commands */ + install_element(OSPF6_NODE, &ospf6_write_multiplier_cmd); + install_element(OSPF6_NODE, &no_ospf6_write_multiplier_cmd); } /* Clear the specified interface structure */ -static void ospf6_interface_clear(struct vty *vty, struct interface *ifp) +void ospf6_interface_clear(struct interface *ifp) { struct ospf6_interface *oi; @@ -2533,7 +2731,7 @@ DEFUN (clear_ipv6_ospf6_interface, if (argc == 4) /* Clear all the ospfv3 interfaces. */ { FOR_ALL_INTERFACES (vrf, ifp) - ospf6_interface_clear(vty, ifp); + ospf6_interface_clear(ifp); } else /* Interface name is specified. */ { if ((ifp = if_lookup_by_name(argv[idx_ifname]->arg, @@ -2543,7 +2741,7 @@ DEFUN (clear_ipv6_ospf6_interface, argv[idx_ifname]->arg); return CMD_WARNING; } - ospf6_interface_clear(vty, ifp); + ospf6_interface_clear(ifp); } return CMD_SUCCESS; |
