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;

Reply via email to