Hi This adds a new feature to ospfd: depend on interface.
A ospfd.conf using it looks like this: --%<-- redistribute default depend on carp0 area 0.0.0.0 { interface em2 { depend on carp0 } [...] } --%<-- This router would send out the default route and the em2 network with default metrics as long as carp0 is master. When carp0 becomes backup these routes are advertised with metric 65535. "depend on" can also be used with other interface types than carp. This diff was started by benno@ at p2k17 (redistribute and config parser). I added the interface part. jca@ contributed several improvements. Comments, OKs? Remi Index: ospfd.c =================================================================== RCS file: /cvs/src/usr.sbin/ospfd/ospfd.c,v retrieving revision 1.94 diff -u -p -r1.94 ospfd.c --- ospfd.c 24 Jan 2017 04:24:25 -0000 1.94 +++ ospfd.c 21 Jan 2018 14:01:42 -0000 @@ -29,6 +29,7 @@ #include <netinet/in.h> #include <arpa/inet.h> +#include <net/if_types.h> #include <event.h> #include <err.h> @@ -512,18 +513,30 @@ imsg_compose_event(struct imsgev *iev, u int ospf_redistribute(struct kroute *kr, u_int32_t *metric) { + struct in_addr addr; + struct kif *kif; struct redistribute *r; - u_int8_t is_default = 0; + int is_default = 0, depend_ok = 1; + + bzero(&addr, sizeof(addr)); /* only allow 0.0.0.0/0 via REDIST_DEFAULT */ if (kr->prefix.s_addr == INADDR_ANY && kr->prefixlen == 0) is_default = 1; SIMPLEQ_FOREACH(r, &ospfd_conf->redist_list, entry) { + if (r->dependon[0] != '\0') { + if ((kif = kif_findname(r->dependon, addr, NULL))) + depend_ok = ifstate_is_up(kif); + else + depend_ok = 0; + } else + depend_ok = 1; + switch (r->type & ~REDIST_NO) { case REDIST_LABEL: if (kr->rtlabel == r->label) { - *metric = r->metric; + *metric = (depend_ok) ? r->metric : MAX_METRIC; return (r->type & REDIST_NO ? 0 : 1); } break; @@ -538,7 +551,7 @@ ospf_redistribute(struct kroute *kr, u_i if (kr->flags & F_DYNAMIC) continue; if (kr->flags & F_STATIC) { - *metric = r->metric; + *metric = (depend_ok) ? r->metric : MAX_METRIC; return (r->type & REDIST_NO ? 0 : 1); } break; @@ -548,7 +561,7 @@ ospf_redistribute(struct kroute *kr, u_i if (kr->flags & F_DYNAMIC) continue; if (kr->flags & F_CONNECTED) { - *metric = r->metric; + *metric = (depend_ok) ? r->metric : MAX_METRIC; return (r->type & REDIST_NO ? 0 : 1); } break; @@ -559,7 +572,8 @@ ospf_redistribute(struct kroute *kr, u_i if (r->addr.s_addr == INADDR_ANY && r->mask.s_addr == INADDR_ANY) { if (is_default) { - *metric = r->metric; + *metric = (depend_ok) ? r->metric : + MAX_METRIC; return (r->type & REDIST_NO ? 0 : 1); } else return (0); @@ -568,13 +582,13 @@ ospf_redistribute(struct kroute *kr, u_i if ((kr->prefix.s_addr & r->mask.s_addr) == (r->addr.s_addr & r->mask.s_addr) && kr->prefixlen >= mask2prefixlen(r->mask.s_addr)) { - *metric = r->metric; + *metric = (depend_ok) ? r->metric : MAX_METRIC; return (r->type & REDIST_NO ? 0 : 1); } break; case REDIST_DEFAULT: if (is_default) { - *metric = r->metric; + *metric = (depend_ok) ? r->metric : MAX_METRIC; return (r->type & REDIST_NO ? 0 : 1); } break; @@ -841,6 +855,10 @@ merge_interfaces(struct area *a, struct if (ospfd_process == PROC_OSPF_ENGINE) if_fsm(i, IF_EVT_UP); } + + strlcpy(i->dependon, xi->dependon, + sizeof(i->dependon)); + i->depend_ok = xi->depend_ok; } return (dirty); } @@ -856,4 +874,15 @@ iface_lookup(struct area *area, struct i i->mask.s_addr == iface->mask.s_addr) return (i); return (NULL); +} + +int +ifstate_is_up(struct kif *kif) +{ + if (!(kif->flags & IFF_UP)) + return (0); + if (kif->if_type == IFT_CARP && + kif->link_state == LINK_STATE_UNKNOWN) + return (0); + return LINK_STATE_IS_UP(kif->link_state); } Index: ospfd.conf.5 =================================================================== RCS file: /cvs/src/usr.sbin/ospfd/ospfd.conf.5,v retrieving revision 1.49 diff -u -p -r1.49 ospfd.conf.5 --- ospfd.conf.5 7 Nov 2017 06:32:02 -0000 1.49 +++ ospfd.conf.5 21 Jan 2018 14:01:42 -0000 @@ -115,14 +115,19 @@ Table 0 is the default table. .Ic default Pc .Sm on .Op Ic set ... +.Op Ic depend on Ar interface .Xc .It Xo .Op Ic no -.Ic redistribute Ar prefix Op Ic set ... +.Ic redistribute Ar prefix +.Op Ic set ... +.Op Ic depend on Ar interface .Xc .It Xo .Op Ic no -.Ic redistribute rtlabel Ar label Op Ic set ... +.Ic redistribute rtlabel Ar label +.Op Ic set ... +.Op Ic depend on Ar interface .Xc If set to .Ic connected , @@ -154,6 +159,14 @@ which will be set no matter what, and ad .Ic no cannot be used together with it. .Pp +With the +.Ic depend on +option, redistributed routes will have a metric of 65535 if the specified +.Ar interface +is down or in state backup. +This is especially useful on a carp cluster to ensure all traffic goes to +the carp master. +.Pp It is possible to set the route .Ic metric and @@ -344,6 +357,9 @@ demotion counter by 1 on the given inter when the interface state is going down. The demotion counter will be decreased when the interface state is active again. +.It Ic depend Ic on Ar interface +A metric of 65535 is used if the specified interface is down or in status +backup. .It Ic fast-hello-interval Ic msec Ar milliseconds If the interface is configured to use .Ic router-dead-time minimal , Index: ospfd.h =================================================================== RCS file: /cvs/src/usr.sbin/ospfd/ospfd.h,v retrieving revision 1.97 diff -u -p -r1.97 ospfd.h --- ospfd.h 24 Jan 2017 04:24:25 -0000 1.97 +++ ospfd.h 21 Jan 2018 14:01:42 -0000 @@ -149,6 +149,7 @@ struct redistribute { u_int32_t metric; u_int16_t label; u_int16_t type; + char dependon[IFNAMSIZ]; }; SIMPLEQ_HEAD(redist_list, redistribute); @@ -325,6 +326,7 @@ struct iface { char name[IF_NAMESIZE]; char demote_group[IFNAMSIZ]; + char dependon[IFNAMSIZ]; char auth_key[MAX_SIMPLE_AUTH_LEN]; struct in_addr addr; struct in_addr dst; @@ -346,6 +348,7 @@ struct iface { int fd; int state; int mtu; + int depend_ok; u_int16_t flags; u_int16_t transmit_delay; u_int16_t hello_interval; @@ -553,6 +556,7 @@ int carp_demote_set(char *, int); /* parse.y */ struct ospfd_conf *parse_config(char *, int); int cmdline_symset(char *); +void conf_clear_redist_list(struct redist_list *); /* in_cksum.c */ u_int16_t in_cksum(void *, size_t); @@ -603,6 +607,7 @@ void merge_config(struct ospfd_conf *, s void imsg_event_add(struct imsgev *); int imsg_compose_event(struct imsgev *, u_int16_t, u_int32_t, pid_t, int, void *, u_int16_t); +int ifstate_is_up(struct kif *kif); /* printconf.c */ void print_config(struct ospfd_conf *); Index: ospfe.c =================================================================== RCS file: /cvs/src/usr.sbin/ospfd/ospfe.c,v retrieving revision 1.99 diff -u -p -r1.99 ospfe.c --- ospfe.c 24 Jan 2017 04:24:25 -0000 1.99 +++ ospfe.c 21 Jan 2018 14:01:42 -0000 @@ -181,10 +181,7 @@ ospfe(struct ospfd_conf *xconf, int pipe event_add(&oeconf->ev, NULL); /* remove unneeded config stuff */ - while ((r = SIMPLEQ_FIRST(&oeconf->redist_list)) != NULL) { - SIMPLEQ_REMOVE_HEAD(&oeconf->redist_list, entry); - free(r); - } + conf_clear_redist_list(&oeconf->redist_list); LIST_FOREACH(area, &oeconf->area_list, entry) { while ((r = SIMPLEQ_FIRST(&area->redist_list)) != NULL) { SIMPLEQ_REMOVE_HEAD(&area->redist_list, entry); @@ -346,6 +343,21 @@ ospfe_dispatch_main(int fd, short event, iface->name); } } + if (strcmp(kif->ifname, + iface->dependon) == 0) { + log_warnx("interface %s" + " changed state, %s" + " depends on it", + kif->ifname, + iface->name); + iface->depend_ok = + ifstate_is_up(kif); + + if ((iface->flags & + IFF_UP) && + LINK_STATE_IS_UP(iface->linkstate)) + orig_rtr_lsa(iface->area); + } } } break; @@ -847,6 +859,9 @@ orig_rtr_lsa(struct area *area) if (oeconf->flags & OSPFD_FLAG_STUB_ROUTER || oe_nofib) rtr_link.metric = MAX_METRIC; + else if (iface->dependon[0] != '\0' && + iface->depend_ok == 0) + rtr_link.metric = MAX_METRIC; else rtr_link.metric = htons(iface->metric); num_links++; @@ -916,12 +931,16 @@ orig_rtr_lsa(struct area *area) rtr_link.num_tos = 0; /* - * backup carp interfaces are anounced with high metric - * for faster failover. + * backup carp interfaces and interfaces that depend + * on an interface that is down are announced with + * high metric for faster failover. */ if (iface->if_type == IFT_CARP && iface->linkstate == LINK_STATE_DOWN) rtr_link.metric = MAX_METRIC; + else if (iface->dependon[0] != '\0' && + iface->depend_ok == 0) + rtr_link.metric = MAX_METRIC; else rtr_link.metric = htons(iface->metric); num_links++; @@ -977,6 +996,9 @@ orig_rtr_lsa(struct area *area) /* RFC 3137: stub router support */ if (oe_nofib || oeconf->flags & OSPFD_FLAG_STUB_ROUTER) + rtr_link.metric = MAX_METRIC; + else if (iface->dependon[0] != '\0' && + iface->depend_ok == 0) rtr_link.metric = MAX_METRIC; else rtr_link.metric = Index: parse.y =================================================================== RCS file: /cvs/src/usr.sbin/ospfd/parse.y,v retrieving revision 1.83 diff -u -p -r1.83 parse.y --- parse.y 5 Jan 2017 13:53:09 -0000 1.83 +++ parse.y 21 Jan 2018 14:01:42 -0000 @@ -132,11 +132,12 @@ typedef struct { %token DEMOTE %token INCLUDE %token ERROR +%token DEPEND ON %token <v.string> STRING %token <v.number> NUMBER %type <v.number> yesno no optlist optlist_l option demotecount msec %type <v.number> deadtime -%type <v.string> string +%type <v.string> string dependon %type <v.redist> redistribute %% @@ -278,7 +279,7 @@ conf_main : ROUTERID STRING { ; -redistribute : no REDISTRIBUTE NUMBER '/' NUMBER optlist { +redistribute : no REDISTRIBUTE NUMBER '/' NUMBER optlist dependon { struct redistribute *r; if ((r = calloc(1, sizeof(*r))) == NULL) @@ -295,9 +296,14 @@ redistribute : no REDISTRIBUTE NUMBER '/ if ($1) r->type |= REDIST_NO; r->metric = $6; + if ($7) { + strlcpy(r->dependon, $7, sizeof(r->dependon)); + } else + r->dependon[0] = '\0'; + free($7); $$ = r; } - | no REDISTRIBUTE STRING optlist { + | no REDISTRIBUTE STRING optlist dependon { struct redistribute *r; if ((r = calloc(1, sizeof(*r))) == NULL) @@ -320,10 +326,15 @@ redistribute : no REDISTRIBUTE NUMBER '/ if ($1) r->type |= REDIST_NO; r->metric = $4; + if ($5) { + strlcpy(r->dependon, $5, sizeof(r->dependon)); + } else + r->dependon[0] = '\0'; free($3); + free($5); $$ = r; } - | no REDISTRIBUTE RTLABEL STRING optlist { + | no REDISTRIBUTE RTLABEL STRING optlist dependon { struct redistribute *r; if ((r = calloc(1, sizeof(*r))) == NULL) @@ -333,19 +344,23 @@ redistribute : no REDISTRIBUTE NUMBER '/ if ($1) r->type |= REDIST_NO; r->metric = $5; + if ($6) + strlcpy(r->dependon, $6, sizeof(r->dependon)); + else + r->dependon[0] = '\0'; free($4); - + free($6); $$ = r; } ; optlist : /* empty */ { $$ = DEFAULT_REDIST_METRIC; } - | SET option { + | SET option { $$ = $2; if (($$ & LSA_METRIC_MASK) == 0) $$ |= DEFAULT_REDIST_METRIC; } - | SET optnl '{' optnl optlist_l optnl '}' { + | SET optnl '{' optnl optlist_l optnl '}' { $$ = $5; if (($$ & LSA_METRIC_MASK) == 0) $$ |= DEFAULT_REDIST_METRIC; @@ -388,6 +403,26 @@ option : METRIC NUMBER { } ; +dependon : /* empty */ { $$ = NULL; } + | DEPEND ON STRING { + struct in_addr addr; + struct kif *kif; + + if (strlen($3) >= IFNAMSIZ) { + yyerror("interface name %s too long", $3); + free($3); + YYERROR; + } + bzero(&addr, sizeof(addr)); + if ((kif = kif_findname($3, addr, NULL)) == NULL) { + yyerror("unknown interface %s", $3); + free($3); + YYERROR; + } + $$ = $3; + } + ; + authmd : AUTHMD NUMBER STRING { if ($2 < MIN_MD_ID || $2 > MAX_MD_ID) { yyerror("auth-md key-id out of range " @@ -693,6 +728,23 @@ interfaceoptsl : PASSIVE { iface->passi YYERROR; } } + | dependon { + struct in_addr addr; + struct kif *kif; + + if ($1) { + strlcpy(iface->dependon, $1, + sizeof(iface->dependon)); + bzero(&addr, sizeof(addr)); + kif = kif_findname($1, addr, NULL); + iface->depend_ok = ifstate_is_up(kif); + } else { + iface->dependon[0] = '\0'; + iface->depend_ok = 1; + } + + free($1); + } | defaults ; @@ -736,6 +788,7 @@ lookup(char *s) {"auth-md-keyid", AUTHMDKEYID}, {"auth-type", AUTHTYPE}, {"demote", DEMOTE}, + {"depend", DEPEND}, {"external-tag", EXTTAG}, {"fast-hello-interval", FASTHELLOINTERVAL}, {"fib-update", FIBUPDATE}, @@ -746,6 +799,7 @@ lookup(char *s) {"minimal", MINIMAL}, {"msec", MSEC}, {"no", NO}, + {"on", ON}, {"passive", PASSIVE}, {"rdomain", RDOMAIN}, {"redistribute", REDISTRIBUTE}, @@ -1288,6 +1342,16 @@ conf_check_rdomain(unsigned int rdomain) } void +conf_clear_redist_list(struct redist_list *rl) +{ + struct redistribute *r; + while ((r = SIMPLEQ_FIRST(rl)) != NULL) { + SIMPLEQ_REMOVE_HEAD(rl, entry); + free(r); + } +} + +void clear_config(struct ospfd_conf *xconf) { struct area *a; @@ -1296,6 +1360,8 @@ clear_config(struct ospfd_conf *xconf) LIST_REMOVE(a, entry); area_del(a); } + + conf_clear_redist_list(&xconf->redist_list); free(xconf); } Index: printconf.c =================================================================== RCS file: /cvs/src/usr.sbin/ospfd/printconf.c,v retrieving revision 1.17 diff -u -p -r1.17 printconf.c --- printconf.c 19 Nov 2016 12:46:46 -0000 1.17 +++ printconf.c 21 Jan 2018 14:01:42 -0000 @@ -94,9 +94,12 @@ print_redistribute(struct redist_list *r printf("%sredistribute default ", print_no(r->type)); break; } - printf("set { metric %d type %d }\n", + printf("set { metric %d type %d }", (r->metric & LSA_METRIC_MASK), ((r->metric & LSA_ASEXT_E_FLAG) == 0 ? 1 : 2)); + if (r->dependon[0]) + printf(" depend on %s", r->dependon); + printf("\n"); } } @@ -120,8 +123,10 @@ print_iface(struct iface *iface) printf("\t\tmetric %d\n", iface->metric); - if (*iface->demote_group) + if (iface->demote_group[0] != '\0') printf("\t\tdemote %s\n", iface->demote_group); + if (iface->dependon[0] != '\0') + printf("\t\tdepend on %s\n", iface->dependon); if (iface->passive) printf("\t\tpassive\n"); else { Index: rde.c =================================================================== RCS file: /cvs/src/usr.sbin/ospfd/rde.c,v retrieving revision 1.108 diff -u -p -r1.108 rde.c --- rde.c 24 Jan 2017 04:24:25 -0000 1.108 +++ rde.c 21 Jan 2018 14:01:42 -0000 @@ -98,7 +98,6 @@ rde(struct ospfd_conf *xconf, int pipe_p struct area *area; struct iface *iface; struct passwd *pw; - struct redistribute *r; pid_t pid; switch (pid = fork()) { @@ -188,10 +187,7 @@ rde(struct ospfd_conf *xconf, int pipe_p LIST_FOREACH(iface, &area->iface_list, entry) md_list_clr(&iface->auth_md_list); - while ((r = SIMPLEQ_FIRST(&rdeconf->redist_list)) != NULL) { - SIMPLEQ_REMOVE_HEAD(&rdeconf->redist_list, entry); - free(r); - } + conf_clear_redist_list(&rdeconf->redist_list); gettimeofday(&now, NULL); rdeconf->uptime = now.tv_sec;