diff --git a/NEWS b/NEWS
index 1341fcd4..4e3349c3 100644
--- a/NEWS
+++ b/NEWS
@@ -1,12 +1,23 @@
-Version 1.3.12 (2013-11-23)
+Version 1.4.0 (2013-11-25)
o BFD protocol (RFC 5880).
o BFD support for OSPF and BGP.
o New 'allow local as' option for BGP.
o Filters allows setting gw, ifname and ifindex.
o Filter operator 'delete/filter' extended to bgp_paths.
o Filter operator 'len' extended to [e]clists.
- o PID file support.
+ o BIRD client now allows shorthands for noninteractive commands.
+ o Flag -P for PID file support.
+ o Flag -f added to force BIRD to run in foreground.
+ o Protocol export/import/receive limits are checked during reconfiguration.
o Several bugfixes and minor improvements.
+ o Several minor but incompatible changes:
+ - IBGP is multihop by default.
+ - Changes primary address selection on BSD to the first one.
+ - Integers in filters are handled as unsigned.
+ - ISO 8601 time formats used by default.
+ - Import of device routes from kernel protocol allowed.
+ - Last state change now tracks just protocol state change.
+ - Minor changes to default router ID calculation.
Version 1.3.11 (2013-07-27)
o OSPF stub router option (RFC 3137).
diff --git a/client/client.c b/client/client.c
index a9d0096d..b938f344 100644
--- a/client/client.c
+++ b/client/client.c
@@ -137,6 +137,21 @@ submit_server_command(char *cmd)
server_send(cmd);
}
+static inline void
+submit_init_command(char *cmd_raw)
+{
+ char *cmd = cmd_expand(cmd_raw);
+
+ if (!cmd)
+ {
+ cleanup();
+ exit(0);
+ }
+
+ submit_server_command(cmd);
+ free(cmd);
+}
+
void
submit_command(char *cmd_raw)
{
@@ -165,7 +180,7 @@ init_commands(void)
{
/* First transition - client received hello from BIRD
and there is waiting initial command */
- submit_server_command(init_cmd);
+ submit_init_command(init_cmd);
init_cmd = NULL;
return;
}
diff --git a/conf/conf.c b/conf/conf.c
index 14225d3b..fc674ef3 100644
--- a/conf/conf.c
+++ b/conf/conf.c
@@ -96,7 +96,8 @@ config_alloc(byte *name)
cfg_mem = c->mem = l;
c->file_name = cfg_strdup(name);
c->load_time = now;
- c->tf_base.fmt1 = c->tf_log.fmt1 = "%d-%m-%Y %T";
+ c->tf_route = c->tf_proto = (struct timeformat){"%T", "%F", 20*3600};
+ c->tf_base = c->tf_log = (struct timeformat){"%F %T", NULL, 0};
return c;
}
diff --git a/doc/bird.sgml b/doc/bird.sgml
index 46d2e026..269743d5 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -256,7 +256,10 @@ which allows you to talk with BIRD in an extensive way.
In the config, everything on a line after /*
*/ is a comment, whitespace characters are treated as a single space. If there's a variable number of options, they are grouped using
the Here is an example of a simple config file. It enables
synchronization of routing tables with OS kernel, scans for
@@ -377,7 +380,7 @@ protocol rip {
" and
"table
Create a new routing table. The default routing table is
@@ -922,9 +929,10 @@ incompatible with each other (that is to prevent you from shooting in the foot).
- local [ Define which AS we
- are part of. (Note that contrary to other IP routers, BIRD is
- able to act as a router located in multiple AS'es
- simultaneously, but in such cases you need to tweak the BGP
- paths manually in the filters to get consistent behavior.)
- Optional local [ Define which AS we are part
+ of. (Note that contrary to other IP routers, BIRD is able to act as a
+ router located in multiple AS'es simultaneously, but in such cases you
+ need to tweak the BGP paths manually in the filters to get consistent
+ behavior.) Optional neighbor Define neighboring router this
+ instance will be talking to and what AS it's located in. In case the
+ neighbor is in the same AS as we are, we automatically switch to iBGP.
This parameter is mandatory.
- neighbor Define neighboring router
- this instance will be talking to and what AS it's located in. Unless
- you use the direct Specify that the neighbor is directly connected. The
+ IP address of the neighbor must be from a directly reachable IP range
+ (i.e. associated with one of your router's interfaces), otherwise the
+ BGP session wouldn't start but it would wait for such interface to
+ appear. The alternative is the multihop [ Configure multihop BGP
- session to a neighbor that isn't directly connected.
- Accurately, this option should be used if the configured
- neighbor IP address does not match with any local network
- subnets. Such IP address have to be reachable through system
- routing table. For multihop BGP it is recommended to
- explicitly configure multihop [ Configure multihop BGP session to a
+ neighbor that isn't directly connected. Accurately, this option should
+ be used if the configured neighbor IP address does not match with any
+ local network subnets. Such IP address have to be reachable through
+ system routing table. The alternative is the source address Define local address we
should use for next hop calculation and as a source address
@@ -1605,8 +1617,8 @@ for each neighbor using the following configuration parameters:
table, and was used in older versions of BIRD, but does not
handle well nontrivial iBGP setups and multihop. Recursive
mode is incompatible with [. Default: . Default: igp table Specifies a table that is used
as an IGP routing table. Default: the same as the table BGP is
diff --git a/doc/reply_codes b/doc/reply_codes
index e9996eef..45b42e00 100644
--- a/doc/reply_codes
+++ b/doc/reply_codes
@@ -53,6 +53,7 @@ Reply codes of BIRD command-line interface
1017 Show ospf lsadb
1018 Show memory
1019 Show ROA list
+1020 Show BFD sessions
8000 Reply too long
8001 Route not found
diff --git a/filter/filter.c b/filter/filter.c
index ed8efd54..88763302 100644
--- a/filter/filter.c
+++ b/filter/filter.c
@@ -90,12 +90,6 @@ pm_format(struct f_path_mask *p, buffer *buf)
buffer_puts(buf, "=]");
}
-static inline int
-int_cmp(int i1, int i2)
-{
- return (i1 > i2) - (i1 < i2);
-}
-
static inline int
uint_cmp(uint i1, uint i2)
{
@@ -146,7 +140,6 @@ val_compare(struct f_val v1, struct f_val v2)
case T_ENUM:
case T_INT:
case T_BOOL:
- return int_cmp(v1.val.i, v2.val.i);
case T_PAIR:
case T_QUAD:
return uint_cmp(v1.val.i, v2.val.i);
@@ -157,7 +150,7 @@ val_compare(struct f_val v1, struct f_val v2)
case T_PREFIX:
if (rc = ipa_compare(v1.val.px.ip, v2.val.px.ip))
return rc;
- return int_cmp(v1.val.px.len, v2.val.px.len);
+ return uint_cmp(v1.val.px.len, v2.val.px.len);
case T_STRING:
return strcmp(v1.val.s, v2.val.s);
default:
@@ -442,16 +435,16 @@ val_format(struct f_val v, buffer *buf)
{
case T_VOID: buffer_puts(buf, "(void)"); return;
case T_BOOL: buffer_puts(buf, v.val.i ? "TRUE" : "FALSE"); return;
- case T_INT: buffer_print(buf, "%d", v.val.i); return;
+ case T_INT: buffer_print(buf, "%u", v.val.i); return;
case T_STRING: buffer_print(buf, "%s", v.val.s); return;
case T_IP: buffer_print(buf, "%I", v.val.px.ip); return;
case T_PREFIX: buffer_print(buf, "%I/%d", v.val.px.ip, v.val.px.len); return;
- case T_PAIR: buffer_print(buf, "(%d,%d)", v.val.i >> 16, v.val.i & 0xffff); return;
+ case T_PAIR: buffer_print(buf, "(%u,%u)", v.val.i >> 16, v.val.i & 0xffff); return;
case T_QUAD: buffer_print(buf, "%R", v.val.i); return;
case T_EC: ec_format(buf2, v.val.ec); buffer_print(buf, "%s", buf2); return;
case T_PREFIX_SET: trie_format(v.val.ti, buf); return;
case T_SET: tree_format(v.val.t, buf); return;
- case T_ENUM: buffer_print(buf, "(enum %x)%d", v.type, v.val.i); return;
+ case T_ENUM: buffer_print(buf, "(enum %x)%u", v.type, v.val.i); return;
case T_PATH: as_path_format(v.val.ad, buf2, 1000); buffer_print(buf, "(path %s)", buf2); return;
case T_CLIST: int_set_format(v.val.ad, 1, -1, buf2, 1000); buffer_print(buf, "(clist %s)", buf2); return;
case T_ECLIST: ec_set_format(v.val.ad, -1, buf2, 1000); buffer_print(buf, "(eclist %s)", buf2); return;
@@ -1167,14 +1160,14 @@ interpret(struct f_inst *what)
/* Community (or cluster) list */
struct f_val dummy;
int arg_set = 0;
- i = 0;
+ uint n = 0;
if ((v2.type == T_PAIR) || (v2.type == T_QUAD))
- i = v2.val.i;
+ n = v2.val.i;
#ifndef IPV6
/* IP->Quad implicit conversion */
else if (v2.type == T_IP)
- i = ipa_to_u32(v2.val.px.ip);
+ n = ipa_to_u32(v2.val.px.ip);
#endif
else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy))
arg_set = 1;
@@ -1190,14 +1183,14 @@ interpret(struct f_inst *what)
if (arg_set == 1)
runtime("Can't add set");
else if (!arg_set)
- res.val.ad = int_set_add(f_pool, v1.val.ad, i);
+ res.val.ad = int_set_add(f_pool, v1.val.ad, n);
else
res.val.ad = int_set_union(f_pool, v1.val.ad, v2.val.ad);
break;
case 'd':
if (!arg_set)
- res.val.ad = int_set_del(f_pool, v1.val.ad, i);
+ res.val.ad = int_set_del(f_pool, v1.val.ad, n);
else
res.val.ad = clist_filter(f_pool, v1.val.ad, v2, 0);
break;
@@ -1502,7 +1495,7 @@ f_run(struct filter *filter, struct rte **rte, struct ea_list **tmp_attrs, struc
log( L_ERR "Filter %s did not return accept nor reject. Make up your mind", filter->name);
return F_ERROR;
}
- DBG( "done (%d)\n", res.val.i );
+ DBG( "done (%u)\n", res.val.i );
return res.val.i;
}
@@ -1519,7 +1512,7 @@ f_eval(struct f_inst *expr, struct linpool *tmp_pool)
return interpret(expr);
}
-int
+uint
f_eval_int(struct f_inst *expr)
{
/* Called independently in parse-time to eval expressions */
diff --git a/filter/filter.h b/filter/filter.h
index 07a4c9e4..3a6b66d9 100644
--- a/filter/filter.h
+++ b/filter/filter.h
@@ -51,7 +51,7 @@ struct f_prefix {
struct f_val {
int type;
union {
- int i;
+ uint i;
u64 ec;
/* ip_addr ip; Folded into prefix */
struct f_prefix px;
@@ -108,7 +108,7 @@ struct rte;
int f_run(struct filter *filter, struct rte **rte, struct ea_list **tmp_attrs, struct linpool *tmp_pool, int flags);
struct f_val f_eval(struct f_inst *expr, struct linpool *tmp_pool);
-int f_eval_int(struct f_inst *expr);
+uint f_eval_int(struct f_inst *expr);
u32 f_eval_asn(struct f_inst *expr);
char *filter_name(struct filter *filter);
diff --git a/misc/bird.spec b/misc/bird.spec
index c27eabbf..162dac55 100644
--- a/misc/bird.spec
+++ b/misc/bird.spec
@@ -1,6 +1,6 @@
Summary: BIRD Internet Routing Daemon
Name: bird
-Version: 1.3.12
+Version: 1.4.0
Release: 1
Copyright: GPL
Group: Networking/Daemons
diff --git a/nest/iface.c b/nest/iface.c
index b4ab70c3..298698a7 100644
--- a/nest/iface.c
+++ b/nest/iface.c
@@ -600,22 +600,10 @@ if_choose_router_id(struct iface_patt *mask, u32 old_id)
if (a->scope <= SCOPE_LINK)
continue;
- /* FIXME: This should go away */
- if (a->flags & IA_PEER)
- continue;
-
- /* FIXME: This should go away too */
- if (!mask && (a != i->addr))
- continue;
-
/* Check pattern if specified */
if (mask && !iface_patt_match(mask, i, a))
continue;
- /* FIXME: This should go away too */
- if ((i->flags & IF_IGNORE) && !mask)
- continue;
-
/* No pattern or pattern matched */
if (!b || ipa_to_u32(a->ip) < ipa_to_u32(b->ip))
b = a;
diff --git a/nest/proto.c b/nest/proto.c
index 019b846e..cfa6ff4b 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -48,7 +48,6 @@ static void
proto_enqueue(list *l, struct proto *p)
{
add_tail(l, &p->n);
- p->last_state_change = now;
}
static void
@@ -363,6 +362,8 @@ proto_init(struct proto_config *c)
q->proto_state = PS_DOWN;
q->core_state = FS_HUNGRY;
+ q->last_state_change = now;
+
proto_enqueue(&initial_proto_list, q);
if (p == &proto_unix_iface)
initial_device_proto = q;
@@ -1084,6 +1085,7 @@ proto_notify_state(struct proto *p, unsigned ps)
return;
p->proto_state = ps;
+ p->last_state_change = now;
switch (ps)
{
diff --git a/proto/bfd/bfd.c b/proto/bfd/bfd.c
index 5ebfadc1..89355483 100644
--- a/proto/bfd/bfd.c
+++ b/proto/bfd/bfd.c
@@ -1070,13 +1070,13 @@ bfd_show_sessions(struct proto *P)
if (p->p.proto_state != PS_UP)
{
- cli_msg(-1013, "%s: is not up", p->p.name);
+ cli_msg(-1020, "%s: is not up", p->p.name);
cli_msg(0, "");
return;
}
- cli_msg(-1013, "%s:", p->p.name);
- cli_msg(-1013, "%-25s %-10s %-10s %-10s %8s %8s",
+ cli_msg(-1020, "%s:", p->p.name);
+ cli_msg(-1020, "%-25s %-10s %-10s %-10s %8s %8s",
"IP address", "Interface", "State", "Since", "Interval", "Timeout");
@@ -1092,7 +1092,7 @@ bfd_show_sessions(struct proto *P)
state = (state < 4) ? state : 0;
tm_format_datetime(tbuf, &config->tf_proto, s->last_state_change);
- cli_msg(-1013, "%-25I %-10s %-10s %-10s %3u.%03u %3u.%03u",
+ cli_msg(-1020, "%-25I %-10s %-10s %-10s %3u.%03u %3u.%03u",
s->addr, ifname, bfd_state_names[state], tbuf,
tx_int / 1000, tx_int % 1000, timeout / 1000, timeout % 1000);
}
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index 81a263bb..f05a85d4 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -1009,6 +1009,24 @@ bgp_check_config(struct bgp_config *c)
if (c->c.class == SYM_TEMPLATE)
return;
+
+ /* EBGP direct by default, IBGP multihop by default */
+ if (c->multihop < 0)
+ c->multihop = internal ? 64 : 0;
+
+ /* Different default for gw_mode */
+ if (!c->gw_mode)
+ c->gw_mode = c->multihop ? GW_RECURSIVE : GW_DIRECT;
+
+ /* Different default based on rs_client */
+ if (!c->missing_lladdr)
+ c->missing_lladdr = c->rs_client ? MLL_IGNORE : MLL_SELF;
+
+ /* Disable after error incompatible with restart limit action */
+ if (c->c.in_limit && (c->c.in_limit->action == PLA_RESTART) && c->disable_after_error)
+ c->c.in_limit->action = PLA_DISABLE;
+
+
if (!c->local_as)
cf_error("Local AS number must be set");
@@ -1024,7 +1042,6 @@ bgp_check_config(struct bgp_config *c)
if (internal && c->rs_client)
cf_error("Only external neighbor can be RS client");
-
if (c->multihop && (c->gw_mode == GW_DIRECT))
cf_error("Multihop BGP cannot use direct gateway mode");
@@ -1035,20 +1052,6 @@ bgp_check_config(struct bgp_config *c)
if (c->multihop && c->bfd && ipa_zero(c->source_addr))
cf_error("Multihop BGP with BFD requires specified source address");
-
- /* Different default based on rs_client */
- if (!c->missing_lladdr)
- c->missing_lladdr = c->rs_client ? MLL_IGNORE : MLL_SELF;
-
- /* Different default for gw_mode */
- if (!c->gw_mode)
- c->gw_mode = (c->multihop || internal) ? GW_RECURSIVE : GW_DIRECT;
-
- /* Disable after error incompatible with restart limit action */
- if (c->c.in_limit && (c->c.in_limit->action == PLA_RESTART) && c->disable_after_error)
- c->c.in_limit->action = PLA_DISABLE;
-
-
if ((c->gw_mode == GW_RECURSIVE) && c->c.table->sorted)
cf_error("BGP in recursive mode prohibits sorted table");
diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y
index ab12fed5..76a76470 100644
--- a/proto/bgp/config.Y
+++ b/proto/bgp/config.Y
@@ -34,6 +34,7 @@ CF_ADDTO(proto, bgp_proto '}' { bgp_check_config(BGP_CFG); } )
bgp_proto_start: proto_start BGP {
this_proto = proto_config_new(&proto_bgp, sizeof(struct bgp_config), $1);
+ BGP_CFG->multihop = -1; /* undefined */
BGP_CFG->hold_time = 240;
BGP_CFG->connect_retry_time = 120;
BGP_CFG->initial_hold_time = 240;
@@ -74,6 +75,7 @@ bgp_proto:
| bgp_proto STARTUP HOLD TIME expr ';' { BGP_CFG->initial_hold_time = $5; }
| bgp_proto CONNECT RETRY TIME expr ';' { BGP_CFG->connect_retry_time = $5; }
| bgp_proto KEEPALIVE TIME expr ';' { BGP_CFG->keepalive_time = $4; }
+ | bgp_proto DIRECT ';' { BGP_CFG->multihop = 0; }
| bgp_proto MULTIHOP ';' { BGP_CFG->multihop = 64; }
| bgp_proto MULTIHOP expr ';' { BGP_CFG->multihop = $3; if (($3<1) || ($3>255)) cf_error("Multihop must be in range 1-255"); }
| bgp_proto NEXT HOP SELF ';' { BGP_CFG->next_hop_self = 1; BGP_CFG->next_hop_keep = 0; }
diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c
index f1409840..333c2a6d 100644
--- a/proto/ospf/iface.c
+++ b/proto/ospf/iface.c
@@ -472,10 +472,14 @@ ospf_iface_stubby(struct ospf_iface_patt *ip, struct ifa *addr)
if (! addr)
return 0;
- /* a host/loopback address */
+ /* a host address */
if (addr->flags & IA_HOST)
return 1;
+ /* a loopback iface */
+ if (addr->iface->flags & IF_LOOPBACK)
+ return 1;
+
/*
* We cannot properly support multiple OSPF ifaces on real iface
* with multiple prefixes, therefore we force OSPF ifaces with
diff --git a/sysdep/bsd/krt-sock.c b/sysdep/bsd/krt-sock.c
index 69a476d9..176e11ed 100644
--- a/sysdep/bsd/krt-sock.c
+++ b/sysdep/bsd/krt-sock.c
@@ -1058,3 +1058,36 @@ kif_sys_shutdown(struct kif_proto *p)
krt_buffer_release(&p->p);
}
+
+struct ifa *
+kif_get_primary_ip(struct iface *i)
+{
+#ifndef IPV6
+ static int fd = -1;
+
+ if (fd < 0)
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+
+ struct ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, i->name, IFNAMSIZ);
+
+ int rv = ioctl(fd, SIOCGIFADDR, (char *) &ifr);
+ if (rv < 0)
+ return NULL;
+
+ ip_addr addr;
+ struct sockaddr_in *sin = (struct sockaddr_in *) &ifr.ifr_addr;
+ memcpy(&addr, &sin->sin_addr.s_addr, sizeof(ip_addr));
+ ipa_ntoh(addr);
+
+ struct ifa *a;
+ WALK_LIST(a, i->addrs)
+ {
+ if (ipa_equal(a->ip, addr))
+ return a;
+ }
+#endif
+
+ return NULL;
+}
diff --git a/sysdep/config.h b/sysdep/config.h
index 914c1090..eb5606ac 100644
--- a/sysdep/config.h
+++ b/sysdep/config.h
@@ -7,7 +7,7 @@
#define _BIRD_CONFIG_H_
/* BIRD version */
-#define BIRD_VERSION "1.3.12"
+#define BIRD_VERSION "1.4.0"
/* Include parameters determined by configure script */
#include "sysdep/autoconf.h"
diff --git a/sysdep/linux/krt-sys.h b/sysdep/linux/krt-sys.h
index 7b3043a7..7e97968a 100644
--- a/sysdep/linux/krt-sys.h
+++ b/sysdep/linux/krt-sys.h
@@ -27,6 +27,8 @@ static inline void kif_sys_postconfig(struct kif_config *c UNUSED) { }
static inline void kif_sys_init_config(struct kif_config *c UNUSED) { }
static inline void kif_sys_copy_config(struct kif_config *d UNUSED, struct kif_config *s UNUSED) { }
+static inline struct ifa * kif_get_primary_ip(struct iface *i) { return NULL; }
+
/* Kernel routes */
diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c
index 90443ed6..ed8769b7 100644
--- a/sysdep/linux/netlink.c
+++ b/sysdep/linux/netlink.c
@@ -862,19 +862,6 @@ nl_parse_route(struct nlmsghdr *h, int scan)
else
{
ra.dest = RTD_DEVICE;
-
- /*
- * In Linux IPv6, 'native' device routes have proto
- * RTPROT_BOOT and not RTPROT_KERNEL (which they have in
- * IPv4 and which is expected). We cannot distinguish
- * 'native' and user defined device routes, so we ignore all
- * such device routes and for consistency, we have the same
- * behavior in IPv4. Anyway, users should use RTPROT_STATIC
- * for their 'alien' routes.
- */
-
- if (i->rtm_protocol == RTPROT_BOOT)
- src = KRT_SRC_KERNEL;
}
break;
diff --git a/sysdep/unix/config.Y b/sysdep/unix/config.Y
index 7bade918..1bba9a0d 100644
--- a/sysdep/unix/config.Y
+++ b/sysdep/unix/config.Y
@@ -14,7 +14,7 @@ CF_HDR
CF_DECLS
CF_KEYWORDS(LOG, SYSLOG, ALL, DEBUG, TRACE, INFO, REMOTE, WARNING, ERROR, AUTH, FATAL, BUG, STDERR, SOFT)
-CF_KEYWORDS(TIMEFORMAT, ISO, SHORT, LONG, BASE, NAME, CONFIRM, UNDO, CHECK, TIMEOUT)
+CF_KEYWORDS(TIMEFORMAT, ISO, OLD, SHORT, LONG, BASE, NAME, CONFIRM, UNDO, CHECK, TIMEOUT)
%type log_mask log_mask_list log_cat cfg_timeout
%type log_file
@@ -96,6 +96,8 @@ timeformat_spec:
| timeformat_which TEXT expr TEXT { *$1 = (struct timeformat){$2, $4, $3}; }
| timeformat_which ISO SHORT { *$1 = (struct timeformat){"%T", "%F", 20*3600}; }
| timeformat_which ISO LONG { *$1 = (struct timeformat){"%F %T", NULL, 0}; }
+ | timeformat_which OLD SHORT { *$1 = (struct timeformat){NULL, NULL, 0}; }
+ | timeformat_which OLD LONG { *$1 = (struct timeformat){"%d-%m-%Y %T", NULL, 0}; }
;
timeformat_base:
diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c
index 3f9e1479..6fdef619 100644
--- a/sysdep/unix/krt.c
+++ b/sysdep/unix/krt.c
@@ -159,6 +159,9 @@ kif_choose_primary(struct iface *i)
return a;
}
+ if (a = kif_get_primary_ip(i))
+ return a;
+
return find_preferred_ifa(i, IPA_NONE, IPA_NONE);
}
diff --git a/sysdep/unix/krt.h b/sysdep/unix/krt.h
index 446914d2..99983ccd 100644
--- a/sysdep/unix/krt.h
+++ b/sysdep/unix/krt.h
@@ -142,5 +142,6 @@ void kif_sys_copy_config(struct kif_config *, struct kif_config *);
void kif_do_scan(struct kif_proto *);
+struct ifa *kif_get_primary_ip(struct iface *i);
#endif
]