mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 09:41:54 +00:00
Temporary integrated commit.
This commit is contained in:
parent
95127cbbb7
commit
b9c8061b19
@ -106,25 +106,15 @@ include ^{WHITE}*include{WHITE}*\".*\"{WHITE}*;
|
||||
{include} { if(cf_open_hook) new_include(); }
|
||||
|
||||
{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+ {
|
||||
#ifdef IPV6
|
||||
if (ipv4_pton_u32(yytext, &cf_lval.i32))
|
||||
return RTRID;
|
||||
if (ip4_pton(yytext, &cf_lval.a4))
|
||||
return IP4;
|
||||
cf_error("Invalid IPv4 address %s", yytext);
|
||||
#else
|
||||
if (ip_pton(yytext, &cf_lval.a))
|
||||
return IPA;
|
||||
cf_error("Invalid IP address %s", yytext);
|
||||
#endif
|
||||
}
|
||||
|
||||
({XIGIT}*::|({XIGIT}*:){3,})({XIGIT}*|{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+) {
|
||||
#ifdef IPV6
|
||||
if (ip_pton(yytext, &cf_lval.a))
|
||||
return IPA;
|
||||
cf_error("Invalid IP address %s", yytext);
|
||||
#else
|
||||
cf_error("This is an IPv4 router, therefore IPv6 addresses are not supported");
|
||||
#endif
|
||||
if (ip6_pton(yytext, &cf_lval.a6))
|
||||
return IP6;
|
||||
cf_error("Invalid IPv6 address %s", yytext);
|
||||
}
|
||||
|
||||
0x{XIGIT}+ {
|
||||
|
@ -117,10 +117,11 @@ config_parse(struct config *c)
|
||||
protos_postconfig(c);
|
||||
if (EMPTY_LIST(c->protos))
|
||||
cf_error("No protocol is specified in the config file");
|
||||
#ifdef IPV6
|
||||
|
||||
/*
|
||||
if (!c->router_id)
|
||||
cf_error("Router ID must be configured manually on IPv6 routers");
|
||||
#endif
|
||||
cf_error("Router ID must be configured manually");
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,8 @@ CF_DECLS
|
||||
int i;
|
||||
u32 i32;
|
||||
ip_addr a;
|
||||
ip4_addr a4;
|
||||
ip6_addr a6;
|
||||
struct symbol *s;
|
||||
char *t;
|
||||
struct rtable_config *r;
|
||||
@ -67,14 +69,15 @@ CF_DECLS
|
||||
%token PO PC
|
||||
%token <i> NUM ENUM
|
||||
%token <i32> RTRID
|
||||
%token <a> IPA
|
||||
%token <a4> IP4
|
||||
%token <a6> IP6
|
||||
%token <s> SYM
|
||||
%token <t> TEXT
|
||||
%type <iface> ipa_scope
|
||||
|
||||
%type <i> expr bool pxlen
|
||||
%type <time> datetime
|
||||
%type <a> ipa
|
||||
%type <a> ipa ipa_raw
|
||||
%type <px> prefix prefix_or_ipa
|
||||
%type <t> text_or_none
|
||||
|
||||
@ -119,7 +122,7 @@ definition:
|
||||
cf_define_symbol($2, SYM_NUMBER, NULL);
|
||||
$2->aux = $4;
|
||||
}
|
||||
| DEFINE SYM '=' IPA ';' {
|
||||
| DEFINE SYM '=' ipa_raw ';' {
|
||||
cf_define_symbol($2, SYM_IPA, cfg_alloc(sizeof(ip_addr)));
|
||||
*(ip_addr *)$2->def = $4;
|
||||
}
|
||||
@ -137,9 +140,15 @@ bool:
|
||||
;
|
||||
|
||||
/* Addresses, prefixes and netmasks */
|
||||
// XXXX check users
|
||||
|
||||
ipa_raw:
|
||||
IP4 { $$ = ipa_from_ip4($1); }
|
||||
| IP6 { $$ = ipa_from_ip6($1); }
|
||||
;
|
||||
|
||||
ipa:
|
||||
IPA
|
||||
ipa_raw
|
||||
| SYM {
|
||||
if ($1->class != SYM_IPA) cf_error("IP address expected");
|
||||
$$ = *(ip_addr *)$1->def;
|
||||
|
53
configure.in
53
configure.in
@ -9,8 +9,6 @@ AC_CONFIG_AUX_DIR(tools)
|
||||
AC_ARG_ENABLE(debug, [ --enable-debug enable internal debugging routines (default: disabled)],,enable_debug=no)
|
||||
AC_ARG_ENABLE(memcheck, [ --enable-memcheck check memory allocations when debugging (default: enabled)],,enable_memcheck=yes)
|
||||
AC_ARG_ENABLE(client, [ --enable-client enable building of BIRD client (default: enabled)],,enable_client=yes)
|
||||
AC_ARG_ENABLE(ipv6, [ --enable-ipv6 enable building of IPv6 version (default: disabled)],,enable_ipv6=no)
|
||||
AC_ARG_WITH(suffix, [ --with-suffix=STRING use specified suffix for BIRD files (default: 6 for IPv6 version)],[given_suffix="yes"])
|
||||
AC_ARG_WITH(sysconfig, [ --with-sysconfig=FILE use specified BIRD system configuration file])
|
||||
AC_ARG_WITH(protocols, [ --with-protocols=LIST include specified routing protocols (default: all)],,[with_protocols="all"])
|
||||
AC_ARG_WITH(sysinclude, [ --with-sysinclude=PATH search for system includes on specified place])
|
||||
@ -44,31 +42,19 @@ AC_SUBST(exedir)
|
||||
AC_SUBST(srcdir_rel_mf)
|
||||
AC_SUBST(runtimedir)
|
||||
|
||||
if test "$enable_ipv6" = yes ; then
|
||||
ip=ipv6
|
||||
SUFFIX=6
|
||||
all_protocols=bgp,ospf,pipe,radv,rip,static
|
||||
else
|
||||
ip=ipv4
|
||||
SUFFIX=""
|
||||
all_protocols=bgp,ospf,pipe,rip,static
|
||||
fi
|
||||
|
||||
if test "$given_suffix" = yes ; then
|
||||
SUFFIX="$with_suffix"
|
||||
fi
|
||||
AC_SUBST(SUFFIX)
|
||||
# all_protocols=bgp,ospf,pipe,radv,rip,static
|
||||
all_protocols=pipe,radv,rip,static
|
||||
|
||||
if test "$with_protocols" = all ; then
|
||||
with_protocols="$all_protocols"
|
||||
fi
|
||||
|
||||
if test "$enable_debug" = yes ; then
|
||||
CONFIG_FILE="bird$SUFFIX.conf"
|
||||
CONTROL_SOCKET="bird$SUFFIX.ctl"
|
||||
CONFIG_FILE="bird.conf"
|
||||
CONTROL_SOCKET="bird.ctl"
|
||||
else
|
||||
CONFIG_FILE="\$(sysconfdir)/bird$SUFFIX.conf"
|
||||
CONTROL_SOCKET="$runtimedir/bird$SUFFIX.ctl"
|
||||
CONFIG_FILE="\$(sysconfdir)/bird.conf"
|
||||
CONTROL_SOCKET="$runtimedir/bird.ctl"
|
||||
fi
|
||||
AC_SUBST(CONFIG_FILE)
|
||||
AC_SUBST(CONTROL_SOCKET)
|
||||
@ -123,32 +109,19 @@ if test -n "$with_sysconfig" -a "$with_sysconfig" != no ; then
|
||||
elif test -f sysconfig.h ; then
|
||||
sysdesc=sysconfig
|
||||
else
|
||||
case "$ip:$host_os" in
|
||||
ipv6:linux*) sysdesc=linux-v6
|
||||
case "$host_os" in
|
||||
linux*) sysdesc=linux
|
||||
default_iproutedir="/etc/iproute2"
|
||||
;;
|
||||
ipv4:linux*) sysdesc=linux
|
||||
default_iproutedir="/etc/iproute2"
|
||||
freebsd*) sysdesc=bsd
|
||||
;;
|
||||
ipv6:netbsd*) sysdesc=bsd-v6
|
||||
kfreebsd*) sysdesc=bsd
|
||||
;;
|
||||
netbsd*) sysdesc=bsd
|
||||
CPPFLAGS="$CPPFLAGS -I/usr/pkg/include"
|
||||
LDFLAGS="$LDFLAGS -L/usr/pkg/lib -R/usr/pkg/lib"
|
||||
;;
|
||||
ipv4:netbsd*) sysdesc=bsd
|
||||
CPPFLAGS="$CPPFLAGS -I/usr/pkg/include"
|
||||
LDFLAGS="$LDFLAGS -L/usr/pkg/lib -R/usr/pkg/lib"
|
||||
;;
|
||||
ipv6:freebsd*) sysdesc=bsd-v6
|
||||
;;
|
||||
ipv4:freebsd*) sysdesc=bsd
|
||||
;;
|
||||
ipv6:kfreebsd*) sysdesc=bsd-v6
|
||||
;;
|
||||
ipv4:kfreebsd*) sysdesc=bsd
|
||||
;;
|
||||
ipv6:openbsd*) sysdesc=bsd-v6
|
||||
;;
|
||||
ipv4:openbsd*) sysdesc=bsd
|
||||
openbsd*) sysdesc=bsd
|
||||
;;
|
||||
*) AC_MSG_ERROR([Cannot determine correct system configuration. Please use --with-sysconfig to set it manually.])
|
||||
;;
|
||||
|
@ -452,7 +452,7 @@ block:
|
||||
* Complex types, their bison value is struct f_val
|
||||
*/
|
||||
fipa:
|
||||
IPA %prec PREFIX_DUMMY { $$.type = T_IP; $$.val.px.ip = $1; }
|
||||
ipa_raw %prec PREFIX_DUMMY { $$.type = T_IP; $$.val.px.ip = $1; }
|
||||
;
|
||||
|
||||
|
||||
@ -541,7 +541,7 @@ switch_items:
|
||||
;
|
||||
|
||||
fprefix_s:
|
||||
IPA '/' NUM %prec '/' {
|
||||
ipa_raw '/' NUM %prec '/' {
|
||||
if (($3 < 0) || ($3 > MAX_PREFIX_LENGTH) || !ip_is_prefix($1, $3)) cf_error("Invalid network prefix: %I/%d.", $1, $3);
|
||||
$$.type = T_PREFIX; $$.val.px.ip = $1; $$.val.px.len = $3;
|
||||
}
|
||||
|
@ -3,13 +3,6 @@ bitops.c
|
||||
bitops.h
|
||||
ip.h
|
||||
ip.c
|
||||
#ifdef IPV6
|
||||
ipv6.c
|
||||
ipv6.h
|
||||
#else
|
||||
ipv4.c
|
||||
ipv4.h
|
||||
#endif
|
||||
lists.c
|
||||
lists.h
|
||||
md5.c
|
||||
|
332
lib/ip.c
332
lib/ip.c
@ -6,6 +6,8 @@
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "lib/ip.h"
|
||||
|
||||
@ -18,6 +20,318 @@
|
||||
* they must be manipulated using the following functions and macros.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
int
|
||||
ip6_compare(ip6_addr a, ip6_addr b)
|
||||
{
|
||||
int i;
|
||||
for(i=0; i<4; i++)
|
||||
if (a.addr[i] > b.addr[i])
|
||||
return 1;
|
||||
else if (a.addr[i] < b.addr[i])
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ip6_addr
|
||||
ip6_mkmask(unsigned n)
|
||||
{
|
||||
ip_addr a;
|
||||
int i;
|
||||
|
||||
for(i=0; i<4; i++)
|
||||
{
|
||||
if (!n)
|
||||
a.addr[i] = 0;
|
||||
else if (n >= 32)
|
||||
{
|
||||
a.addr[i] = ~0;
|
||||
n -= 32;
|
||||
}
|
||||
else
|
||||
{
|
||||
a.addr[i] = u32_mkmask(n);
|
||||
n = 0;
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
unsigned
|
||||
ip6_masklen(ip_addr *a)
|
||||
{
|
||||
int i, j, n;
|
||||
|
||||
for(i=0, n=0; i<4; i++, n+=32)
|
||||
if (a->addr[i] != ~0U)
|
||||
{
|
||||
j = u32_masklen(a->addr[i]);
|
||||
if (j < 0)
|
||||
return j;
|
||||
n += j;
|
||||
while (++i < 4)
|
||||
if (a->addr[i])
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
int
|
||||
ip4_classify(ip4_addr ad)
|
||||
{
|
||||
u32 a = _I(ad);
|
||||
u32 b = a >> 24U;
|
||||
|
||||
if (b && b <= 0xdf)
|
||||
{
|
||||
if (b == 0x7f)
|
||||
return IADDR_HOST | SCOPE_HOST;
|
||||
else if (b == 0x0a ||
|
||||
(a & 0xffff0000) == 0xc0a80000 ||
|
||||
(a & 0xfff00000) == 0xac100000)
|
||||
return IADDR_HOST | SCOPE_SITE;
|
||||
else
|
||||
return IADDR_HOST | SCOPE_UNIVERSE;
|
||||
}
|
||||
if (b >= 0xe0 && b <= 0xef)
|
||||
return IADDR_MULTICAST | SCOPE_UNIVERSE;
|
||||
if (a == 0xffffffff)
|
||||
return IADDR_BROADCAST | SCOPE_LINK;
|
||||
return IADDR_INVALID;
|
||||
}
|
||||
|
||||
int
|
||||
ip6_classify(ip6_addr *a)
|
||||
{
|
||||
u32 x = a->addr[0];
|
||||
|
||||
if ((x & 0xe0000000) == 0x20000000) /* 2000::/3 Aggregatable Global Unicast Address */
|
||||
return IADDR_HOST | SCOPE_UNIVERSE;
|
||||
if ((x & 0xffc00000) == 0xfe800000) /* fe80::/10 Link-Local Address */
|
||||
return IADDR_HOST | SCOPE_LINK;
|
||||
if ((x & 0xffc00000) == 0xfec00000) /* fec0::/10 Site-Local Address */
|
||||
return IADDR_HOST | SCOPE_SITE;
|
||||
if ((x & 0xfe000000) == 0xfc000000) /* fc00::/7 Unique Local Unicast Address (RFC 4193) */
|
||||
return IADDR_HOST | SCOPE_SITE;
|
||||
if ((x & 0xff000000) == 0xff000000) /* ff00::/8 Multicast Address */
|
||||
{
|
||||
unsigned int scope = (x >> 16) & 0x0f;
|
||||
switch (scope)
|
||||
{
|
||||
case 1: return IADDR_MULTICAST | SCOPE_HOST;
|
||||
case 2: return IADDR_MULTICAST | SCOPE_LINK;
|
||||
case 5: return IADDR_MULTICAST | SCOPE_SITE;
|
||||
case 8: return IADDR_MULTICAST | SCOPE_ORGANIZATION;
|
||||
case 14: return IADDR_MULTICAST | SCOPE_UNIVERSE;
|
||||
default: return IADDR_MULTICAST | SCOPE_UNDEFINED;
|
||||
}
|
||||
}
|
||||
if (!x && !a->addr[1])
|
||||
{
|
||||
u32 a2 = a->addr[2];
|
||||
u32 a3 = a->addr[3];
|
||||
if (a2 == 0 && a3 == 1)
|
||||
return IADDR_HOST | SCOPE_HOST; /* Loopback address */
|
||||
if (a2 == 0)
|
||||
return ip4_classify(_MI4(a3)); /* IPv4 compatible addresses */
|
||||
if (a2 == 0xffff)
|
||||
return ip4_classify(_MI4(a3)); /* IPv4 mapped addresses */
|
||||
|
||||
return IADDR_INVALID;
|
||||
}
|
||||
|
||||
return IADDR_HOST | SCOPE_UNDEFINED;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Conversion of IPv6 address to presentation format and vice versa.
|
||||
* Heavily inspired by routines written by Paul Vixie for the BIND project
|
||||
* and of course by RFC 2373.
|
||||
*/
|
||||
|
||||
|
||||
char *
|
||||
ip4_ntop(ip4_addr a, char *b)
|
||||
{
|
||||
u32 x = _I(a);
|
||||
return b + bsprintf(b, "%d.%d.%d.%d", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff);
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
ip6_ntop(ip6_addr a, char *b)
|
||||
{
|
||||
u16 words[8];
|
||||
int bestpos, bestlen, curpos, curlen, i;
|
||||
|
||||
/* First of all, preprocess the address and find the longest run of zeros */
|
||||
bestlen = bestpos = curpos = curlen = 0;
|
||||
for(i=0; i<8; i++)
|
||||
{
|
||||
u32 x = a.addr[i/2];
|
||||
words[i] = ((i%2) ? x : (x >> 16)) & 0xffff;
|
||||
if (words[i])
|
||||
curlen = 0;
|
||||
else
|
||||
{
|
||||
if (!curlen)
|
||||
curpos = i;
|
||||
curlen++;
|
||||
if (curlen > bestlen)
|
||||
{
|
||||
bestpos = curpos;
|
||||
bestlen = curlen;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bestlen < 2)
|
||||
bestpos = -1;
|
||||
|
||||
/* Is it an encapsulated IPv4 address? */
|
||||
if (!bestpos &&
|
||||
(bestlen == 5 && a.addr[2] == 0xffff ||
|
||||
bestlen == 6))
|
||||
{
|
||||
u32 x = a.addr[3];
|
||||
b += bsprintf(b, "::%s%d.%d.%d.%d",
|
||||
a.addr[2] ? "ffff:" : "",
|
||||
(x >> 24) & 0xff,
|
||||
(x >> 16) & 0xff,
|
||||
(x >> 8) & 0xff,
|
||||
x & 0xff);
|
||||
return b;
|
||||
}
|
||||
|
||||
/* Normal IPv6 formatting, compress the largest sequence of zeros */
|
||||
for(i=0; i<8; i++)
|
||||
{
|
||||
if (i == bestpos)
|
||||
{
|
||||
i += bestlen - 1;
|
||||
*b++ = ':';
|
||||
if (i == 7)
|
||||
*b++ = ':';
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i)
|
||||
*b++ = ':';
|
||||
b += bsprintf(b, "%x", words[i]);
|
||||
}
|
||||
}
|
||||
*b = 0;
|
||||
return b;
|
||||
}
|
||||
|
||||
int
|
||||
ip4_pton(char *a, ip4_addr *o)
|
||||
{
|
||||
int i;
|
||||
unsigned long int l;
|
||||
u32 ia = 0;
|
||||
|
||||
i=4;
|
||||
while (i--)
|
||||
{
|
||||
char *d, *c = strchr(a, '.');
|
||||
if (!c != !i)
|
||||
return 0;
|
||||
l = strtoul(a, &d, 10);
|
||||
if (d != c && *d || l > 255)
|
||||
return 0;
|
||||
ia = (ia << 8) | l;
|
||||
if (c)
|
||||
c++;
|
||||
a = c;
|
||||
}
|
||||
*o = ip4_from_u32(ia);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
ip6_pton(char *a, ip6_addr *o)
|
||||
{
|
||||
u16 words[8];
|
||||
int i, j, k, l, hfil;
|
||||
char *start;
|
||||
|
||||
if (a[0] == ':') /* Leading :: */
|
||||
{
|
||||
if (a[1] != ':')
|
||||
return 0;
|
||||
a++;
|
||||
}
|
||||
hfil = -1;
|
||||
i = 0;
|
||||
while (*a)
|
||||
{
|
||||
if (*a == ':') /* :: */
|
||||
{
|
||||
if (hfil >= 0)
|
||||
return 0;
|
||||
hfil = i;
|
||||
a++;
|
||||
continue;
|
||||
}
|
||||
j = 0;
|
||||
l = 0;
|
||||
start = a;
|
||||
for(;;)
|
||||
{
|
||||
if (*a >= '0' && *a <= '9')
|
||||
k = *a++ - '0';
|
||||
else if (*a >= 'A' && *a <= 'F')
|
||||
k = *a++ - 'A' + 10;
|
||||
else if (*a >= 'a' && *a <= 'f')
|
||||
k = *a++ - 'a' + 10;
|
||||
else
|
||||
break;
|
||||
j = (j << 4) + k;
|
||||
if (j >= 0x10000 || ++l > 4)
|
||||
return 0;
|
||||
}
|
||||
if (*a == ':' && a[1])
|
||||
a++;
|
||||
else if (*a == '.' && (i == 6 || i < 6 && hfil >= 0))
|
||||
{ /* Embedded IPv4 address */
|
||||
ip4_addr x;
|
||||
if (!ip4_pton(start, &x))
|
||||
return 0;
|
||||
words[i++] = _I(x) >> 16;
|
||||
words[i++] = _I(x);
|
||||
break;
|
||||
}
|
||||
else if (*a)
|
||||
return 0;
|
||||
if (i >= 8)
|
||||
return 0;
|
||||
words[i++] = j;
|
||||
}
|
||||
|
||||
/* Replace :: with an appropriate number of zeros */
|
||||
if (hfil >= 0)
|
||||
{
|
||||
j = 8 - i;
|
||||
for(i=7; i-j >= hfil; i--)
|
||||
words[i] = words[i-j];
|
||||
for(; i>=hfil; i--)
|
||||
words[i] = 0;
|
||||
}
|
||||
|
||||
/* Convert the address to ip6_addr format */
|
||||
for(i=0; i<4; i++)
|
||||
o->addr[i] = (words[2*i] << 16) | words[2*i+1];
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ip_scope_text - get textual representation of address scope
|
||||
* @scope: scope (%SCOPE_xxx)
|
||||
@ -35,6 +349,9 @@ ip_scope_text(unsigned scope)
|
||||
return scope_table[scope];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* ipa_equal - compare two IP addresses for equality
|
||||
@ -193,7 +510,7 @@ ip_addr ipa_to_u32(u32 x) { DUMMY }
|
||||
int ipa_compare(ip_addr x, ip_addr y) { DUMMY }
|
||||
|
||||
/**
|
||||
* ipa_build - build an IPv6 address from parts
|
||||
* ipa_build6 - build an IPv6 address from parts
|
||||
* @a1: part #1
|
||||
* @a2: part #2
|
||||
* @a3: part #3
|
||||
@ -203,18 +520,7 @@ int ipa_compare(ip_addr x, ip_addr y) { DUMMY }
|
||||
* address. It's used for example when a protocol wants to bind its
|
||||
* socket to a hard-wired multicast address.
|
||||
*/
|
||||
ip_addr ipa_build(u32 a1, u32 a2, u32 a3, u32 a4) { DUMMY }
|
||||
|
||||
/**
|
||||
* ipa_absolutize - convert link scope IPv6 address to universe scope
|
||||
* @x: link scope IPv6 address
|
||||
* @y: universe scope IPv6 prefix of the interface
|
||||
*
|
||||
* This function combines a link-scope IPv6 address @x with the universe
|
||||
* scope prefix @x of the network assigned to an interface to get a
|
||||
* universe scope form of @x.
|
||||
*/
|
||||
ip_addr ipa_absolutize(ip_addr x, ip_addr y) { DUMMY }
|
||||
ip_addr ipa_build6(u32 a1, u32 a2, u32 a3, u32 a4) { DUMMY }
|
||||
|
||||
/**
|
||||
* ip_ntop - convert IP address to textual representation
|
||||
|
385
lib/ip.h
385
lib/ip.h
@ -9,32 +9,70 @@
|
||||
#ifndef _BIRD_IP_H_
|
||||
#define _BIRD_IP_H_
|
||||
|
||||
#ifndef IPV6
|
||||
#include "ipv4.h"
|
||||
#include "lib/endian.h"
|
||||
#include "lib/string.h"
|
||||
#include "lib/bitops.h"
|
||||
#include "lib/unaligned.h"
|
||||
|
||||
|
||||
#ifdef DEBUGGING
|
||||
|
||||
/*
|
||||
* Use the structural representation when you want to make sure
|
||||
* nobody unauthorized attempts to handle ip_addr as number.
|
||||
*/
|
||||
|
||||
typedef struct ip4_addr {
|
||||
u32 addr;
|
||||
} ip4_addr;
|
||||
|
||||
#define _MI4(x) ((struct ip4_addr) { x })
|
||||
#define _I(x) (x).addr
|
||||
|
||||
#else
|
||||
#include "ipv6.h"
|
||||
|
||||
typedef u32 ip4_addr;
|
||||
|
||||
#define _MI4(x) (x)
|
||||
#define _I(x) (x)
|
||||
|
||||
#endif
|
||||
|
||||
#define ipa_zero(x) (!ipa_nonzero(x))
|
||||
#define ip_is_prefix(a,l) (!ipa_nonzero(ipa_and(a, ipa_not(ipa_mkmask(l)))))
|
||||
#define ipa_in_net(x,n,p) (ipa_zero(ipa_and(ipa_xor((n),(x)),ipa_mkmask(p))))
|
||||
#define net_in_net(n1,l1,n2,l2) (((l1) >= (l2)) && (ipa_zero(ipa_and(ipa_xor((n1),(n2)),ipa_mkmask(l2)))))
|
||||
|
||||
typedef struct ip6_addr {
|
||||
u32 addr[4];
|
||||
} ip6_addr;
|
||||
|
||||
#define _MI6(a,b,c,d) ((struct ip6_addr) {{ a, b, c, d }})
|
||||
#define _I0(a) ((a).addr[0])
|
||||
#define _I1(a) ((a).addr[1])
|
||||
#define _I2(a) ((a).addr[2])
|
||||
#define _I3(a) ((a).addr[3])
|
||||
|
||||
|
||||
typedef ip6_addr ip_addr;
|
||||
|
||||
|
||||
|
||||
#define IPA_NONE IP6_NONE
|
||||
#define IP4_NONE _MI4(0)
|
||||
#define IP6_NONE _MI6(0,0,0,0)
|
||||
|
||||
|
||||
/*
|
||||
* ip_classify() returns either a negative number for invalid addresses
|
||||
* or scope OR'ed together with address type.
|
||||
*/
|
||||
|
||||
#define IADDR_INVALID -1
|
||||
#define IADDR_SCOPE_MASK 0xfff
|
||||
#define IADDR_HOST 0x1000
|
||||
#define IADDR_BROADCAST 0x2000
|
||||
#define IADDR_MULTICAST 0x4000
|
||||
|
||||
|
||||
/*
|
||||
* Address scope
|
||||
*/
|
||||
|
||||
#define SCOPE_HOST 0
|
||||
#define SCOPE_LINK 1
|
||||
#define SCOPE_SITE 2
|
||||
@ -42,26 +80,341 @@
|
||||
#define SCOPE_UNIVERSE 4
|
||||
#define SCOPE_UNDEFINED 5
|
||||
|
||||
char *ip_scope_text(unsigned);
|
||||
|
||||
|
||||
#define ipa_equal(x,y) ip6_equal(x,y)
|
||||
#define ipa_zero(x) ip6_zero(x)
|
||||
#define ipa_nonzero(x) ip6_nonzero(x)
|
||||
#define ipa_and(x,y) ip6_and(x,y)
|
||||
#define ipa_or(x,y) ip6_or(x,y)
|
||||
#define ipa_xor(x,y) ip6_xor(x,y)
|
||||
#define ipa_not(x) ip6_not(x)
|
||||
|
||||
#define ip4_equal(x,y) (_I(x) == _I(y))
|
||||
#define ip4_zero(x) (!_I(x))
|
||||
#define ip4_nonzero(x) _I(x)
|
||||
#define ip4_and(x,y) _MI4(_I(x) & _I(y))
|
||||
#define ip4_or(x,y) _MI4(_I(x) | _I(y))
|
||||
#define ip4_xor(x,y) _MI4(_I(x) ^ _I(y))
|
||||
#define ip4_not(x) _MI4(~_I(x))
|
||||
|
||||
static inline int ip6_equal(ip6_addr a, ip6_addr b)
|
||||
{ return _I0(a) == _I0(b) && _I1(a) == _I1(b) && _I2(a) == _I2(b) && _I3(a) == _I3(b); }
|
||||
|
||||
static inline int ip6_zero(ip6_addr a)
|
||||
{ return !_I0(a) && !_I1(a) && !_I2(a) && !_I3(a); }
|
||||
|
||||
static inline int ip6_nonzero(ip6_addr a)
|
||||
{ return _I0(a) || _I1(a) || _I2(a) || _I3(a); }
|
||||
|
||||
static inline ip6_addr ip6_and(ip6_addr a, ip6_addr b)
|
||||
{ return _MI6(_I0(a) & _I0(b), _I1(a) & _I1(b), _I2(a) & _I2(b), _I3(a) & _I3(b)); }
|
||||
|
||||
static inline ip6_addr ip6_or(ip6_addr a, ip6_addr b)
|
||||
{ return _MI6(_I0(a) | _I0(b), _I1(a) | _I1(b), _I2(a) | _I2(b), _I3(a) | _I3(b)); }
|
||||
|
||||
static inline ip6_addr ip6_xor(ip6_addr a, ip6_addr b)
|
||||
{ return _MI6(_I0(a) ^ _I0(b), _I1(a) ^ _I1(b), _I2(a) ^ _I2(b), _I3(a) ^ _I3(b)); }
|
||||
|
||||
static inline ip6_addr ip6_not(ip6_addr a)
|
||||
{ return _MI6(~_I0(a), ~_I1(a), ~_I2(a), ~_I3(a)); }
|
||||
|
||||
|
||||
|
||||
#define ipa_from_ip4(x) _MI6(0,0,0xffff,_I(x))
|
||||
#define ipa_from_ip6(x) x
|
||||
|
||||
#define ipa_to_ip4(x) _I3(x)
|
||||
#define ipa_to_ip6(x) x
|
||||
|
||||
#define ip4_from_u32(x) _MI4(x)
|
||||
#define ip4_to_u32(x) _I(x)
|
||||
|
||||
#define ipa_is_ip4(a) ip6_is_v4mapped(a)
|
||||
|
||||
#define ipa_build4(a,b,c,d) ipa_from_ip4(ip4_build(a,b,c,d))
|
||||
#define ipa_build6(a,b,c,d) _MI6(a,b,c,d)
|
||||
|
||||
#define ip4_build(a,b,c,d) _MI4(((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
|
||||
#define ip6_build(a,b,c,d) _MI6(a,b,c,d)
|
||||
|
||||
|
||||
|
||||
#define ipa_hton(x) x = ip6_hton(x)
|
||||
#define ipa_ntoh(x) x = ip6_ntoh(x)
|
||||
|
||||
#define ip4_hton(x) _MI4(htonl(_I(x)))
|
||||
#define ip4_ntoh(x) _MI4(ntohl(_I(x)))
|
||||
|
||||
static inline ip6_addr ip6_hton(ip6_addr a)
|
||||
{ return _MI6(htonl(_I0(a)), htonl(_I1(a)), htonl(_I2(a)), htonl(_I3(a))); }
|
||||
|
||||
static inline ip6_addr ip6_ntoh(ip6_addr a)
|
||||
{ return _MI6(ntohl(_I0(a)), ntohl(_I1(a)), ntohl(_I2(a)), ntohl(_I3(a))); }
|
||||
|
||||
|
||||
|
||||
#define ipa_compare(a,b) ip6_compare(a,b)
|
||||
|
||||
static inline int ip4_compare(ip4_addr a, ip4_addr b)
|
||||
{ return (_I(a) > _I(b)) - (_I(a) < _I(b)); }
|
||||
|
||||
int ip6_compare(ip6_addr a, ip6_addr b);
|
||||
|
||||
|
||||
|
||||
#define ipa_hash(a) ip6_hash(a)
|
||||
|
||||
static inline unsigned ip4_hash(ip4_addr a)
|
||||
{
|
||||
/* Returns a 16-bit value */
|
||||
u32 x = _I(a);
|
||||
x ^= x >> 16;
|
||||
x ^= x << 10;
|
||||
return x & 0xffff;
|
||||
}
|
||||
|
||||
/*
|
||||
* Network prefixes
|
||||
* This hash function looks well, but once IPv6 enters
|
||||
* mainstream use, we need to check that it has good
|
||||
* distribution properties on real routing tables.
|
||||
*/
|
||||
|
||||
static inline unsigned ip6_hash(ip6_addr a)
|
||||
{
|
||||
/* Returns a 16-bit hash key */
|
||||
u32 x = _I0(a) ^ _I1(a) ^ _I2(a) ^ _I3(a);
|
||||
return (x ^ (x >> 16) ^ (x >> 8)) & 0xffff;
|
||||
}
|
||||
|
||||
|
||||
#define ipa_classify(x) ip6_classify(&(x))
|
||||
int ip4_classify(ip4_addr ad);
|
||||
int ip6_classify(ip6_addr *a);
|
||||
|
||||
#define ipa_is_link_local(a) ip6_is_link_local(a)
|
||||
|
||||
static inline int ip6_is_link_local(ip6_addr a)
|
||||
{ return (_I0(a) & 0xffc00000) == 0xfe800000; }
|
||||
|
||||
static inline int ip6_is_v4mapped(ip6_addr a)
|
||||
{ return _I0(a) == 0 && _I1(a) == 0 && _I2(a) == 0xffff; }
|
||||
|
||||
|
||||
|
||||
#define ipa_mkmask(x) ip6_mkmask(x)
|
||||
#define ipa_mklen(x) ip6_masklen(&x) // XXXX: ipa_masklen()
|
||||
|
||||
#define ip4_mkmask(x) _MI4(u32_mkmask(x))
|
||||
#define ip4_masklen(x) u32_masklen(_I(x))
|
||||
|
||||
ip6_addr ip6_mkmask(unsigned n);
|
||||
unsigned ip6_masklen(ip_addr *a); // XXXX: int or unsigned?
|
||||
|
||||
|
||||
|
||||
/* ipa_pxlen() requires that x != y */
|
||||
#define ipa_pxlen(a,b) ip6_pxlen(a,b)
|
||||
|
||||
static inline u32 ip4_pxlen(ip4_addr a, ip4_addr b)
|
||||
{ return 31 - u32_log2(_I(a) ^ _I(b)); }
|
||||
|
||||
static inline u32 ip6_pxlen(ip6_addr a, ip6_addr b)
|
||||
{
|
||||
int i = 0;
|
||||
i+= (a.addr[i] == b.addr[i]);
|
||||
i+= (a.addr[i] == b.addr[i]);
|
||||
i+= (a.addr[i] == b.addr[i]);
|
||||
i+= (a.addr[i] == b.addr[i]);
|
||||
return 32 * i + 31 - u32_log2(a.addr[i] ^ b.addr[i]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define ipa_opposite_m1(x) ip6_opposite_m1(x)
|
||||
#define ipa_opposite_m2(x) ip6_opposite_m2(x)
|
||||
|
||||
#define ip4_opposite_m1(x) _MI4(_I(x) ^ 1)
|
||||
#define ip4_opposite_m2(x) _MI4(_I(x) ^ 3)
|
||||
|
||||
static inline ip6_addr ip6_opposite_m1(ip6_addr a)
|
||||
{ return _MI6(_I0(a), _I1(a), _I2(a), _I3(a) ^ 1); }
|
||||
|
||||
static inline ip6_addr ip6_opposite_m2(ip6_addr a)
|
||||
{ return _MI6(_I0(a), _I1(a), _I2(a), _I3(a) ^ 3); }
|
||||
|
||||
|
||||
|
||||
// XXXX
|
||||
#define ipa_getbit(a,y) ip6_getbit(a,y)
|
||||
|
||||
static inline u32 ip4_getbit(ip4_addr a, u32 pos)
|
||||
{ return _I(a) & (0x80000000 >> pos); }
|
||||
|
||||
static inline u32 ip6_getbit(ip6_addr a, u32 pos)
|
||||
{ return a.addr[pos / 32] & (0x80000000 >> (pos % 32)); }
|
||||
|
||||
|
||||
// XXXX
|
||||
#define ipa_put_addr(buf,a) ip6_put(buf,a)
|
||||
|
||||
static inline void * ip4_put(void *buf, ip4_addr a)
|
||||
{
|
||||
put_u32(buf, _I(a));
|
||||
return buf+4;
|
||||
}
|
||||
|
||||
static inline void * ip6_put(void *buf, ip6_addr a)
|
||||
{
|
||||
a = ip6_hton(a);
|
||||
memcpy(buf, &a, 16);
|
||||
return buf+16;
|
||||
}
|
||||
|
||||
static inline ip4_addr ip4_get(void *buf)
|
||||
{
|
||||
return _MI4(get_u32(buf));
|
||||
}
|
||||
|
||||
static inline ip6_addr ip6_get(void *buf)
|
||||
{
|
||||
ip6_addr a;
|
||||
memcpy(&a, buf, 16);
|
||||
return ip6_ntoh(a);
|
||||
}
|
||||
|
||||
static inline void * ip4_put32(void *buf, ip4_addr a)
|
||||
{
|
||||
*(u32 *)buf = htonl(_I(a));
|
||||
return buf+4;
|
||||
}
|
||||
|
||||
static inline void * ip6_put32(void *buf, ip6_addr a)
|
||||
{
|
||||
u32 *b = buf;
|
||||
b[0] = htonl(_I0(a));
|
||||
b[1] = htonl(_I1(a));
|
||||
b[2] = htonl(_I2(a));
|
||||
b[3] = htonl(_I3(a));
|
||||
return buf+16;
|
||||
}
|
||||
|
||||
static inline void * ip6_put32_ip4(void *buf, ip6_addr a)
|
||||
{
|
||||
*(u32 *)buf = htonl(_I3(a));
|
||||
return buf+4;
|
||||
}
|
||||
|
||||
static inline ip6_addr ipa_get_in4(struct in_addr *in)
|
||||
{ return ipa_from_ip4(ip4_ntoh(*(ip4_addr *) in)); }
|
||||
|
||||
static inline ip6_addr ipa_get_in6(struct in6_addr *in)
|
||||
{ return ip6_ntoh(*(ip6_addr *) in); }
|
||||
|
||||
// XXXX check callers
|
||||
static inline void ipa_put_in4(struct in_addr *in, ip6_addr a)
|
||||
{ ip6_put32_ip4(in, a); }
|
||||
|
||||
static inline void ipa_put_in6(struct in6_addr *in, ip6_addr a)
|
||||
{ ip6_put32(in, a); }
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Conversions between internal and string representation
|
||||
*/
|
||||
|
||||
char *ip4_ntop(ip4_addr a, char *b);
|
||||
char *ip6_ntop(ip6_addr a, char *b);
|
||||
|
||||
static inline char * ip4_ntox(ip4_addr a, char *b)
|
||||
{ return b + bsprintf(b, "%08x", _I(a)); }
|
||||
|
||||
static inline char * ip6_ntox(ip6_addr a, char *b)
|
||||
{ return b + bsprintf(b, "%08x.%08x.%08x.%08x", _I0(a), _I1(a), _I2(a), _I3(a)); }
|
||||
|
||||
int ip4_pton(char *a, ip4_addr *o);
|
||||
int ip6_pton(char *a, ip6_addr *o);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// XXXX process rest
|
||||
|
||||
struct prefix {
|
||||
ip_addr addr;
|
||||
unsigned int len;
|
||||
};
|
||||
|
||||
#define ip_is_prefix(a,l) (!ipa_nonzero(ipa_and(a, ipa_not(ipa_mkmask(l)))))
|
||||
#define ipa_in_net(x,n,p) (ipa_zero(ipa_and(ipa_xor((n),(x)),ipa_mkmask(p))))
|
||||
#define net_in_net(n1,l1,n2,l2) (((l1) >= (l2)) && (ipa_zero(ipa_and(ipa_xor((n1),(n2)),ipa_mkmask(l2)))))
|
||||
|
||||
char *ip_scope_text(unsigned);
|
||||
|
||||
/*
|
||||
* Network prefixes
|
||||
*/
|
||||
|
||||
static inline int ipa_classify_net(ip_addr a)
|
||||
{ return ipa_zero(a) ? (IADDR_HOST | SCOPE_UNIVERSE) : ipa_classify(a); }
|
||||
|
||||
/*
|
||||
* Conversions between internal and string representation
|
||||
*/
|
||||
#define MAX_PREFIX_LENGTH 32
|
||||
#define BITS_PER_IP_ADDRESS 32
|
||||
#define STD_ADDRESS_P_LENGTH 15
|
||||
#define SIZE_OF_IP_HEADER 24
|
||||
*/
|
||||
|
||||
char *ip_ntop(ip_addr a, char *);
|
||||
char *ip_ntox(ip_addr a, char *);
|
||||
int ip_pton(char *a, ip_addr *o);
|
||||
|
||||
|
||||
#define MAX_PREFIX_LENGTH 128
|
||||
#define BITS_PER_IP_ADDRESS 128
|
||||
#define STD_ADDRESS_P_LENGTH 39
|
||||
#define SIZE_OF_IP_HEADER 40
|
||||
|
||||
#define ipa_class_mask(x) _MI4(ipv4_class_mask(_I(x)))
|
||||
|
||||
|
||||
/*
|
||||
#define ip_skip_header(x, y) ipv4_skip_header(x, y)
|
||||
|
||||
#define IP_PREC_INTERNET_CONTROL 0xc0
|
||||
*/
|
||||
|
||||
u32 ipv4_class_mask(u32);
|
||||
byte *ipv4_skip_header(byte *, int *);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* In IPv6, SOCK_RAW does not return packet header */
|
||||
#define ip_skip_header(x, y) x
|
||||
|
||||
|
||||
/*
|
||||
* RFC 1883 defines packet precendece, but RFC 2460 replaces it
|
||||
* by generic Traffic Class ID with no defined semantics. Better
|
||||
* not use it yet.
|
||||
*/
|
||||
#define IP_PREC_INTERNET_CONTROL -1
|
||||
|
||||
#endif
|
||||
|
110
lib/ipv4.c
110
lib/ipv4.c
@ -1,110 +0,0 @@
|
||||
/*
|
||||
* BIRD Library -- IPv4 Address Manipulation Functions
|
||||
*
|
||||
* (c) 1998 Martin Mares <mj@ucw.cz>
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "lib/ip.h"
|
||||
#include "lib/string.h"
|
||||
|
||||
int
|
||||
ipv4_classify(u32 a)
|
||||
{
|
||||
u32 b = a >> 24U;
|
||||
|
||||
if (b && b <= 0xdf)
|
||||
{
|
||||
if (b == 0x7f)
|
||||
return IADDR_HOST | SCOPE_HOST;
|
||||
else if (b == 0x0a ||
|
||||
(a & 0xffff0000) == 0xc0a80000 ||
|
||||
(a & 0xfff00000) == 0xac100000)
|
||||
return IADDR_HOST | SCOPE_SITE;
|
||||
else
|
||||
return IADDR_HOST | SCOPE_UNIVERSE;
|
||||
}
|
||||
if (b >= 0xe0 && b <= 0xef)
|
||||
return IADDR_MULTICAST | SCOPE_UNIVERSE;
|
||||
if (a == 0xffffffff)
|
||||
return IADDR_BROADCAST | SCOPE_LINK;
|
||||
return IADDR_INVALID;
|
||||
}
|
||||
|
||||
char *
|
||||
ip_ntop(ip_addr a, char *b)
|
||||
{
|
||||
u32 x = _I(a);
|
||||
|
||||
return b + bsprintf(b, "%d.%d.%d.%d",
|
||||
((x >> 24) & 0xff),
|
||||
((x >> 16) & 0xff),
|
||||
((x >> 8) & 0xff),
|
||||
(x & 0xff));
|
||||
}
|
||||
|
||||
char *
|
||||
ip_ntox(ip_addr a, char *b)
|
||||
{
|
||||
return b + bsprintf(b, "%08x", _I(a));
|
||||
}
|
||||
|
||||
u32
|
||||
ipv4_class_mask(u32 a)
|
||||
{
|
||||
u32 m;
|
||||
|
||||
if (a < 0x80000000)
|
||||
m = 0xff000000;
|
||||
else if (a < 0xc0000000)
|
||||
m = 0xffff0000;
|
||||
else
|
||||
m = 0xffffff00;
|
||||
while (a & ~m)
|
||||
m |= m >> 1;
|
||||
return m;
|
||||
}
|
||||
|
||||
int
|
||||
ip_pton(char *a, ip_addr *o)
|
||||
{
|
||||
int i;
|
||||
unsigned long int l;
|
||||
u32 ia = 0;
|
||||
|
||||
i=4;
|
||||
while (i--)
|
||||
{
|
||||
char *d, *c = strchr(a, '.');
|
||||
if (!c != !i)
|
||||
return 0;
|
||||
l = strtoul(a, &d, 10);
|
||||
if (d != c && *d || l > 255)
|
||||
return 0;
|
||||
ia = (ia << 8) | l;
|
||||
if (c)
|
||||
c++;
|
||||
a = c;
|
||||
}
|
||||
*o = ipa_from_u32(ia);
|
||||
return 1;
|
||||
}
|
||||
|
||||
byte *
|
||||
ipv4_skip_header(byte *pkt, int *len)
|
||||
{
|
||||
int l = *len;
|
||||
int q;
|
||||
|
||||
if (l < 20 || (*pkt & 0xf0) != 0x40)
|
||||
return NULL;
|
||||
q = (*pkt & 0x0f) * 4;
|
||||
if (q > l)
|
||||
return NULL;
|
||||
*len -= q;
|
||||
return pkt + q;
|
||||
}
|
107
lib/ipv4.h
107
lib/ipv4.h
@ -1,107 +0,0 @@
|
||||
/*
|
||||
* BIRD -- IP Addresses et Cetera for IPv4
|
||||
*
|
||||
* (c) 1998--1999 Martin Mares <mj@ucw.cz>
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#ifndef _BIRD_IPV4_H_
|
||||
#define _BIRD_IPV4_H_
|
||||
|
||||
#include "lib/endian.h"
|
||||
#include "lib/bitops.h"
|
||||
#include "lib/unaligned.h"
|
||||
|
||||
#ifdef DEBUGGING
|
||||
|
||||
/*
|
||||
* Use the structural representation when you want to make sure
|
||||
* nobody unauthorized attempts to handle ip_addr as number.
|
||||
*/
|
||||
|
||||
typedef struct ipv4_addr {
|
||||
u32 addr;
|
||||
} ip_addr;
|
||||
|
||||
#define _I(x) (x).addr
|
||||
#define _MI(x) ((struct ipv4_addr) { x })
|
||||
|
||||
#else
|
||||
|
||||
typedef u32 ip_addr;
|
||||
|
||||
#define _I(x) (x)
|
||||
#define _MI(x) (x)
|
||||
|
||||
#endif
|
||||
|
||||
#define MAX_PREFIX_LENGTH 32
|
||||
#define BITS_PER_IP_ADDRESS 32
|
||||
#define STD_ADDRESS_P_LENGTH 15
|
||||
#define SIZE_OF_IP_HEADER 24
|
||||
|
||||
#define IPA_NONE (_MI(0))
|
||||
|
||||
#define ipa_equal(x,y) (_I(x) == _I(y))
|
||||
#define ipa_nonzero(x) _I(x)
|
||||
#define ipa_and(x,y) _MI(_I(x) & _I(y))
|
||||
#define ipa_or(x,y) _MI(_I(x) | _I(y))
|
||||
#define ipa_xor(x,y) _MI(_I(x) ^ _I(y))
|
||||
#define ipa_not(x) _MI(~_I(x))
|
||||
#define ipa_mkmask(x) _MI(u32_mkmask(x))
|
||||
#define ipa_mklen(x) u32_masklen(_I(x))
|
||||
#define ipa_hash(x) ipv4_hash(_I(x))
|
||||
#define ipa_hton(x) x = _MI(htonl(_I(x)))
|
||||
#define ipa_ntoh(x) x = _MI(ntohl(_I(x)))
|
||||
#define ipa_classify(x) ipv4_classify(_I(x))
|
||||
#define ipa_has_link_scope(x) ipv4_has_link_scope(_I(x))
|
||||
#define ipa_opposite_m1(x) _MI(_I(x) ^ 1)
|
||||
#define ipa_opposite_m2(x) _MI(_I(x) ^ 3)
|
||||
#define ipa_class_mask(x) _MI(ipv4_class_mask(_I(x)))
|
||||
#define ipa_from_u32(x) _MI(x)
|
||||
#define ipa_to_u32(x) _I(x)
|
||||
#define ipa_compare(x,y) ipv4_compare(_I(x),_I(y))
|
||||
/* ipa_pxlen() requires that x != y */
|
||||
#define ipa_pxlen(x, y) ipv4_pxlen(_I(x), _I(y))
|
||||
#define ipa_getbit(x, y) (_I(x) & (0x80000000 >> (y)))
|
||||
#define ipa_put_addr(x, y) ipv4_put_addr(x, y)
|
||||
|
||||
#define ip_skip_header(x, y) ipv4_skip_header(x, y)
|
||||
|
||||
int ipv4_classify(u32);
|
||||
u32 ipv4_class_mask(u32);
|
||||
byte *ipv4_skip_header(byte *, int *);
|
||||
|
||||
static inline int ipv4_has_link_scope(u32 a UNUSED)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline unsigned ipv4_hash(u32 a)
|
||||
{
|
||||
/* Returns a 16-bit value */
|
||||
a ^= a >> 16;
|
||||
a ^= a << 10;
|
||||
return a & 0xffff;
|
||||
}
|
||||
|
||||
static inline int ipv4_compare(u32 x, u32 y)
|
||||
{
|
||||
return (x > y) - (x < y);
|
||||
}
|
||||
|
||||
static inline u32 ipv4_pxlen(u32 a, u32 b)
|
||||
{
|
||||
return 31 - u32_log2(a ^ b);
|
||||
}
|
||||
|
||||
static inline byte * ipv4_put_addr(byte *buf, ip_addr a)
|
||||
{
|
||||
put_u32(buf, _I(a));
|
||||
return buf+4;
|
||||
}
|
||||
|
||||
#define IP_PREC_INTERNET_CONTROL 0xc0
|
||||
|
||||
#endif
|
384
lib/ipv6.c
384
lib/ipv6.c
@ -1,384 +0,0 @@
|
||||
/*
|
||||
* BIRD Library -- IPv6 Address Manipulation Functions
|
||||
*
|
||||
* (c) 1999 Martin Mares <mj@ucw.cz>
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "lib/ip.h"
|
||||
#include "lib/bitops.h"
|
||||
#include "lib/endian.h"
|
||||
#include "lib/string.h"
|
||||
|
||||
/*
|
||||
* See RFC 2373 for explanation of IPv6 addressing issues.
|
||||
*/
|
||||
|
||||
ip_addr
|
||||
ipv6_mkmask(unsigned n)
|
||||
{
|
||||
ip_addr a;
|
||||
int i;
|
||||
|
||||
for(i=0; i<4; i++)
|
||||
{
|
||||
if (!n)
|
||||
a.addr[i] = 0;
|
||||
else if (n >= 32)
|
||||
{
|
||||
a.addr[i] = ~0;
|
||||
n -= 32;
|
||||
}
|
||||
else
|
||||
{
|
||||
a.addr[i] = u32_mkmask(n);
|
||||
n = 0;
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
unsigned
|
||||
ipv6_mklen(ip_addr *a)
|
||||
{
|
||||
int i, j, n;
|
||||
|
||||
for(i=0, n=0; i<4; i++, n+=32)
|
||||
if (a->addr[i] != ~0U)
|
||||
{
|
||||
j = u32_masklen(a->addr[i]);
|
||||
if (j < 0)
|
||||
return j;
|
||||
n += j;
|
||||
while (++i < 4)
|
||||
if (a->addr[i])
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
int
|
||||
ipv6_classify(ip_addr *a)
|
||||
{
|
||||
u32 x = a->addr[0];
|
||||
|
||||
if ((x & 0xe0000000) == 0x20000000) /* 2000::/3 Aggregatable Global Unicast Address */
|
||||
return IADDR_HOST | SCOPE_UNIVERSE;
|
||||
if ((x & 0xffc00000) == 0xfe800000) /* fe80::/10 Link-Local Address */
|
||||
return IADDR_HOST | SCOPE_LINK;
|
||||
if ((x & 0xffc00000) == 0xfec00000) /* fec0::/10 Site-Local Address */
|
||||
return IADDR_HOST | SCOPE_SITE;
|
||||
if ((x & 0xfe000000) == 0xfc000000) /* fc00::/7 Unique Local Unicast Address (RFC 4193) */
|
||||
return IADDR_HOST | SCOPE_SITE;
|
||||
if ((x & 0xff000000) == 0xff000000) /* ff00::/8 Multicast Address */
|
||||
{
|
||||
unsigned int scope = (x >> 16) & 0x0f;
|
||||
switch (scope)
|
||||
{
|
||||
case 1: return IADDR_MULTICAST | SCOPE_HOST;
|
||||
case 2: return IADDR_MULTICAST | SCOPE_LINK;
|
||||
case 5: return IADDR_MULTICAST | SCOPE_SITE;
|
||||
case 8: return IADDR_MULTICAST | SCOPE_ORGANIZATION;
|
||||
case 14: return IADDR_MULTICAST | SCOPE_UNIVERSE;
|
||||
default: return IADDR_MULTICAST | SCOPE_UNDEFINED;
|
||||
}
|
||||
}
|
||||
if (!x && !a->addr[1] && !a->addr[2])
|
||||
{
|
||||
u32 y = a->addr[3];
|
||||
if (y == 1)
|
||||
return IADDR_HOST | SCOPE_HOST; /* Loopback address */
|
||||
/* IPv4 compatible addresses */
|
||||
if (y >= 0x7f000000 && y < 0x80000000)
|
||||
return IADDR_HOST | SCOPE_HOST;
|
||||
if ((y & 0xff000000) == 0x0a000000 ||
|
||||
(y & 0xffff0000) == 0xc0a80000 ||
|
||||
(y & 0xfff00000) == 0xac100000)
|
||||
return IADDR_HOST | SCOPE_SITE;
|
||||
if (y >= 0x01000000 && y < 0xe0000000)
|
||||
return IADDR_HOST | SCOPE_UNIVERSE;
|
||||
}
|
||||
return IADDR_HOST | SCOPE_UNDEFINED;
|
||||
}
|
||||
|
||||
void
|
||||
ipv6_hton(ip_addr *a)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; i<4; i++)
|
||||
a->addr[i] = htonl(a->addr[i]);
|
||||
}
|
||||
|
||||
void
|
||||
ipv6_ntoh(ip_addr *a)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; i<4; i++)
|
||||
a->addr[i] = ntohl(a->addr[i]);
|
||||
}
|
||||
|
||||
int
|
||||
ipv6_compare(ip_addr X, ip_addr Y)
|
||||
{
|
||||
int i;
|
||||
ip_addr *x = &X;
|
||||
ip_addr *y = &Y;
|
||||
|
||||
for(i=0; i<4; i++)
|
||||
if (x->addr[i] > y->addr[i])
|
||||
return 1;
|
||||
else if (x->addr[i] < y->addr[i])
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Conversion of IPv6 address to presentation format and vice versa.
|
||||
* Heavily inspired by routines written by Paul Vixie for the BIND project
|
||||
* and of course by RFC 2373.
|
||||
*/
|
||||
|
||||
char *
|
||||
ip_ntop(ip_addr a, char *b)
|
||||
{
|
||||
u16 words[8];
|
||||
int bestpos, bestlen, curpos, curlen, i;
|
||||
|
||||
/* First of all, preprocess the address and find the longest run of zeros */
|
||||
bestlen = bestpos = curpos = curlen = 0;
|
||||
for(i=0; i<8; i++)
|
||||
{
|
||||
u32 x = a.addr[i/2];
|
||||
words[i] = ((i%2) ? x : (x >> 16)) & 0xffff;
|
||||
if (words[i])
|
||||
curlen = 0;
|
||||
else
|
||||
{
|
||||
if (!curlen)
|
||||
curpos = i;
|
||||
curlen++;
|
||||
if (curlen > bestlen)
|
||||
{
|
||||
bestpos = curpos;
|
||||
bestlen = curlen;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bestlen < 2)
|
||||
bestpos = -1;
|
||||
|
||||
/* Is it an encapsulated IPv4 address? */
|
||||
if (!bestpos &&
|
||||
(bestlen == 5 && a.addr[2] == 0xffff ||
|
||||
bestlen == 6))
|
||||
{
|
||||
u32 x = a.addr[3];
|
||||
b += bsprintf(b, "::%s%d.%d.%d.%d",
|
||||
a.addr[2] ? "ffff:" : "",
|
||||
((x >> 24) & 0xff),
|
||||
((x >> 16) & 0xff),
|
||||
((x >> 8) & 0xff),
|
||||
(x & 0xff));
|
||||
return b;
|
||||
}
|
||||
|
||||
/* Normal IPv6 formatting, compress the largest sequence of zeros */
|
||||
for(i=0; i<8; i++)
|
||||
{
|
||||
if (i == bestpos)
|
||||
{
|
||||
i += bestlen - 1;
|
||||
*b++ = ':';
|
||||
if (i == 7)
|
||||
*b++ = ':';
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i)
|
||||
*b++ = ':';
|
||||
b += bsprintf(b, "%x", words[i]);
|
||||
}
|
||||
}
|
||||
*b = 0;
|
||||
return b;
|
||||
}
|
||||
|
||||
char *
|
||||
ip_ntox(ip_addr a, char *b)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; i<4; i++)
|
||||
{
|
||||
if (i)
|
||||
*b++ = '.';
|
||||
b += bsprintf(b, "%08x", a.addr[i]);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
int
|
||||
ipv4_pton_u32(char *a, u32 *o)
|
||||
{
|
||||
int i;
|
||||
unsigned long int l;
|
||||
u32 ia = 0;
|
||||
|
||||
i=4;
|
||||
while (i--)
|
||||
{
|
||||
char *d, *c = strchr(a, '.');
|
||||
if (!c != !i)
|
||||
return 0;
|
||||
l = strtoul(a, &d, 10);
|
||||
if (d != c && *d || l > 255)
|
||||
return 0;
|
||||
ia = (ia << 8) | l;
|
||||
if (c)
|
||||
c++;
|
||||
a = c;
|
||||
}
|
||||
*o = ia;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
ip_pton(char *a, ip_addr *o)
|
||||
{
|
||||
u16 words[8];
|
||||
int i, j, k, l, hfil;
|
||||
char *start;
|
||||
|
||||
if (a[0] == ':') /* Leading :: */
|
||||
{
|
||||
if (a[1] != ':')
|
||||
return 0;
|
||||
a++;
|
||||
}
|
||||
hfil = -1;
|
||||
i = 0;
|
||||
while (*a)
|
||||
{
|
||||
if (*a == ':') /* :: */
|
||||
{
|
||||
if (hfil >= 0)
|
||||
return 0;
|
||||
hfil = i;
|
||||
a++;
|
||||
continue;
|
||||
}
|
||||
j = 0;
|
||||
l = 0;
|
||||
start = a;
|
||||
for(;;)
|
||||
{
|
||||
if (*a >= '0' && *a <= '9')
|
||||
k = *a++ - '0';
|
||||
else if (*a >= 'A' && *a <= 'F')
|
||||
k = *a++ - 'A' + 10;
|
||||
else if (*a >= 'a' && *a <= 'f')
|
||||
k = *a++ - 'a' + 10;
|
||||
else
|
||||
break;
|
||||
j = (j << 4) + k;
|
||||
if (j >= 0x10000 || ++l > 4)
|
||||
return 0;
|
||||
}
|
||||
if (*a == ':' && a[1])
|
||||
a++;
|
||||
else if (*a == '.' && (i == 6 || i < 6 && hfil >= 0))
|
||||
{ /* Embedded IPv4 address */
|
||||
u32 x;
|
||||
if (!ipv4_pton_u32(start, &x))
|
||||
return 0;
|
||||
words[i++] = x >> 16;
|
||||
words[i++] = x;
|
||||
break;
|
||||
}
|
||||
else if (*a)
|
||||
return 0;
|
||||
if (i >= 8)
|
||||
return 0;
|
||||
words[i++] = j;
|
||||
}
|
||||
|
||||
/* Replace :: with an appropriate number of zeros */
|
||||
if (hfil >= 0)
|
||||
{
|
||||
j = 8 - i;
|
||||
for(i=7; i-j >= hfil; i--)
|
||||
words[i] = words[i-j];
|
||||
for(; i>=hfil; i--)
|
||||
words[i] = 0;
|
||||
}
|
||||
|
||||
/* Convert the address to ip_addr format */
|
||||
for(i=0; i<4; i++)
|
||||
o->addr[i] = (words[2*i] << 16) | words[2*i+1];
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ipv6_absolutize(ip_addr *a, ip_addr *ifa)
|
||||
{
|
||||
if ((a->addr[0] & 0xffc00000) == 0xfe800000 && /* a is link-scope */
|
||||
((ifa->addr[0] & 0xe0000000) == 0x20000000 | /* ifa is AGU ... */
|
||||
(ifa->addr[0] & 0xffc00000) == 0xfec00000)) /* ... or site-scope */
|
||||
{
|
||||
a->addr[0] = ifa->addr[0]; /* Copy the prefix, leave interface ID */
|
||||
a->addr[1] = ifa->addr[1];
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
|
||||
#include "bitops.c"
|
||||
|
||||
static void test(char *x)
|
||||
{
|
||||
ip_addr a;
|
||||
char c[STD_ADDRESS_P_LENGTH+1];
|
||||
|
||||
printf("%-40s ", x);
|
||||
if (!ip_pton(x, &a))
|
||||
{
|
||||
puts("BAD");
|
||||
return;
|
||||
}
|
||||
ip_ntop(a, c);
|
||||
printf("%-40s %04x\n", c, ipv6_classify(&a));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
puts("Positive tests:");
|
||||
test("1:2:3:4:5:6:7:8");
|
||||
test("dead:beef:DEAD:BEEF::f00d");
|
||||
test("::");
|
||||
test("::1");
|
||||
test("1::");
|
||||
test("::1.234.5.6");
|
||||
test("::ffff:1.234.5.6");
|
||||
test("::fffe:1.234.5.6");
|
||||
test("1:2:3:4:5:6:7::8");
|
||||
test("2080::8:800:200c:417a");
|
||||
test("ff01::101");
|
||||
|
||||
puts("Negative tests:");
|
||||
test(":::");
|
||||
test("1:2:3:4:5:6:7:8:");
|
||||
test("1::2::3");
|
||||
test("::12345");
|
||||
test("::1.2.3.4:5");
|
||||
test(":1:2:3:4:5:6:7:8");
|
||||
test("g:1:2:3:4:5:6:7");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
138
lib/ipv6.h
138
lib/ipv6.h
@ -1,138 +0,0 @@
|
||||
|
||||
/*
|
||||
* BIRD -- IP Addresses et Cetera for IPv6
|
||||
*
|
||||
* (c) 1999--2000 Martin Mares <mj@ucw.cz>
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#ifndef _BIRD_IPV6_H_
|
||||
#define _BIRD_IPV6_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include "lib/string.h"
|
||||
#include "lib/bitops.h"
|
||||
#include "lib/unaligned.h"
|
||||
|
||||
typedef struct ipv6_addr {
|
||||
u32 addr[4];
|
||||
} ip_addr;
|
||||
|
||||
#define _MI(a,b,c,d) ((struct ipv6_addr) {{ a, b, c, d }})
|
||||
#define _I0(a) ((a).addr[0])
|
||||
#define _I1(a) ((a).addr[1])
|
||||
#define _I2(a) ((a).addr[2])
|
||||
#define _I3(a) ((a).addr[3])
|
||||
|
||||
#define MAX_PREFIX_LENGTH 128
|
||||
#define BITS_PER_IP_ADDRESS 128
|
||||
#define STD_ADDRESS_P_LENGTH 39
|
||||
#define SIZE_OF_IP_HEADER 40
|
||||
|
||||
#define IPA_NONE _MI(0,0,0,0)
|
||||
|
||||
#define ipa_equal(x,y) ({ ip_addr _a=(x), _b=(y); \
|
||||
_I0(_a) == _I0(_b) && \
|
||||
_I1(_a) == _I1(_b) && \
|
||||
_I2(_a) == _I2(_b) && \
|
||||
_I3(_a) == _I3(_b); })
|
||||
#define ipa_nonzero(x) ({ ip_addr _a=(x); (_I0(_a) || _I1(_a) || _I2(_a) || _I3(_a)); })
|
||||
#define ipa_and(x,y) ({ ip_addr _a=(x), _b=(y); \
|
||||
_MI(_I0(_a) & _I0(_b), \
|
||||
_I1(_a) & _I1(_b), \
|
||||
_I2(_a) & _I2(_b), \
|
||||
_I3(_a) & _I3(_b)); })
|
||||
#define ipa_or(x,y) ({ ip_addr _a=(x), _b=(y); \
|
||||
_MI(_I0(_a) | _I0(_b), \
|
||||
_I1(_a) | _I1(_b), \
|
||||
_I2(_a) | _I2(_b), \
|
||||
_I3(_a) | _I3(_b)); })
|
||||
#define ipa_xor(x,y) ({ ip_addr _a=(x), _b=(y); \
|
||||
_MI(_I0(_a) ^ _I0(_b), \
|
||||
_I1(_a) ^ _I1(_b), \
|
||||
_I2(_a) ^ _I2(_b), \
|
||||
_I3(_a) ^ _I3(_b)); })
|
||||
#define ipa_not(x) ({ ip_addr _a=(x); _MI(~_I0(_a),~_I1(_a),~_I2(_a),~_I3(_a)); })
|
||||
#define ipa_mkmask(x) ipv6_mkmask(x)
|
||||
#define ipa_mklen(x) ipv6_mklen(&(x))
|
||||
#define ipa_hash(x) ipv6_hash(&(x))
|
||||
#define ipa_hton(x) ipv6_hton(&(x))
|
||||
#define ipa_ntoh(x) ipv6_ntoh(&(x))
|
||||
#define ipa_classify(x) ipv6_classify(&(x))
|
||||
#define ipa_has_link_scope(x) ipv6_has_link_scope(&(x))
|
||||
#define ipa_opposite_m1(x) ({ ip_addr _a=(x); _MI(_I0(_a),_I1(_a),_I2(_a),_I3(_a) ^ 1); })
|
||||
#define ipa_opposite_m2(x) ({ ip_addr _a=(x); _MI(_I0(_a),_I1(_a),_I2(_a),_I3(_a) ^ 3); })
|
||||
/* ipa_class_mask don't make sense with IPv6 */
|
||||
/* ipa_from_u32 and ipa_to_u32 replaced by ipa_build */
|
||||
#define ipa_build(a,b,c,d) _MI(a,b,c,d)
|
||||
#define ipa_compare(x,y) ipv6_compare(x,y)
|
||||
/* ipa_pxlen() requires that x != y */
|
||||
#define ipa_pxlen(x, y) ipv6_pxlen(x, y)
|
||||
#define ipa_getbit(x, y) ipv6_getbit(x, y)
|
||||
#define ipa_put_addr(x, y) ipv6_put_addr(x, y)
|
||||
#define ipa_absolutize(x,y) ipv6_absolutize(x,y)
|
||||
|
||||
/* In IPv6, SOCK_RAW does not return packet header */
|
||||
#define ip_skip_header(x, y) x
|
||||
|
||||
ip_addr ipv6_mkmask(unsigned);
|
||||
unsigned ipv6_mklen(ip_addr *);
|
||||
int ipv6_classify(ip_addr *);
|
||||
void ipv6_hton(ip_addr *);
|
||||
void ipv6_ntoh(ip_addr *);
|
||||
int ipv6_compare(ip_addr, ip_addr);
|
||||
int ipv4_pton_u32(char *, u32 *);
|
||||
void ipv6_absolutize(ip_addr *, ip_addr *);
|
||||
|
||||
static inline int ipv6_has_link_scope(ip_addr *a)
|
||||
{
|
||||
return ((a->addr[0] & 0xffc00000) == 0xfe800000);
|
||||
}
|
||||
|
||||
/*
|
||||
* This hash function looks well, but once IPv6 enters
|
||||
* mainstream use, we need to check that it has good
|
||||
* distribution properties on real routing tables.
|
||||
*/
|
||||
|
||||
static inline unsigned ipv6_hash(ip_addr *a)
|
||||
{
|
||||
/* Returns a 16-bit hash key */
|
||||
u32 x = _I0(*a) ^ _I1(*a) ^ _I2(*a) ^ _I3(*a);
|
||||
return (x ^ (x >> 16) ^ (x >> 8)) & 0xffff;
|
||||
}
|
||||
|
||||
static inline u32 ipv6_getbit(ip_addr a, u32 y)
|
||||
{
|
||||
return a.addr[y / 32] & (0x80000000 >> (y % 32));
|
||||
}
|
||||
|
||||
static inline u32 ipv6_pxlen(ip_addr a, ip_addr b)
|
||||
{
|
||||
int i = 0;
|
||||
i+= (a.addr[i] == b.addr[i]);
|
||||
i+= (a.addr[i] == b.addr[i]);
|
||||
i+= (a.addr[i] == b.addr[i]);
|
||||
i+= (a.addr[i] == b.addr[i]);
|
||||
return 32 * i + 31 - u32_log2(a.addr[i] ^ b.addr[i]);
|
||||
}
|
||||
|
||||
static inline byte * ipv6_put_addr(byte *buf, ip_addr a)
|
||||
{
|
||||
put_u32(buf+0, _I0(a));
|
||||
put_u32(buf+4, _I1(a));
|
||||
put_u32(buf+8, _I2(a));
|
||||
put_u32(buf+12, _I3(a));
|
||||
return buf+16;
|
||||
}
|
||||
|
||||
/*
|
||||
* RFC 1883 defines packet precendece, but RFC 2460 replaces it
|
||||
* by generic Traffic Class ID with no defined semantics. Better
|
||||
* not use it yet.
|
||||
*/
|
||||
#define IP_PREC_INTERNET_CONTROL -1
|
||||
|
||||
#endif
|
11
lib/printf.c
11
lib/printf.c
@ -273,9 +273,10 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
|
||||
/* IP address */
|
||||
case 'I':
|
||||
if (flags & SPECIAL)
|
||||
ip_ntox(va_arg(args, ip_addr), ipbuf);
|
||||
ip6_ntox(va_arg(args, ip_addr), ipbuf);
|
||||
else {
|
||||
ip_ntop(va_arg(args, ip_addr), ipbuf);
|
||||
// XXXX update IPv4 / IPv6 distinction
|
||||
ip6_ntop(va_arg(args, ip_addr), ipbuf);
|
||||
if (field_width > 0)
|
||||
field_width = STD_ADDRESS_P_LENGTH;
|
||||
}
|
||||
@ -300,11 +301,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
|
||||
/* Router/Network ID - essentially IPv4 address in u32 value */
|
||||
case 'R':
|
||||
x = va_arg(args, u32);
|
||||
bsprintf(ipbuf, "%d.%d.%d.%d",
|
||||
((x >> 24) & 0xff),
|
||||
((x >> 16) & 0xff),
|
||||
((x >> 8) & 0xff),
|
||||
(x & 0xff));
|
||||
ip4_ntop(ip4_from_u32(x), ipbuf);
|
||||
s = ipbuf;
|
||||
goto str;
|
||||
|
||||
|
27
lib/socket.h
27
lib/socket.h
@ -43,6 +43,7 @@ typedef struct birdsock {
|
||||
/* laddr and lifindex are valid only if SKF_LADDR_RX flag is set to request it */
|
||||
|
||||
int fd; /* System-dependent data */
|
||||
int af; /* Address family (AF_INET, AF_INET6 or 0 for non-IP) of fd */
|
||||
node n;
|
||||
void *rbuf_alloc, *tbuf_alloc;
|
||||
char *password; /* Password for MD5 authentication */
|
||||
@ -66,25 +67,27 @@ int sk_setup_multicast(sock *s);
|
||||
int sk_join_group(sock *s, ip_addr maddr);
|
||||
int sk_leave_group(sock *s, ip_addr maddr);
|
||||
|
||||
#ifdef IPV6
|
||||
int sk_set_ipv6_checksum(sock *s, int offset);
|
||||
int sk_set_icmp_filter(sock *s, int p1, int p2);
|
||||
#endif
|
||||
|
||||
int sk_set_icmp6_filter(sock *s, int p1, int p2);
|
||||
int sk_set_broadcast(sock *s, int enable);
|
||||
|
||||
static inline int
|
||||
sk_send_buffer_empty(sock *sk)
|
||||
{
|
||||
return sk->tbuf == sk->tpos;
|
||||
}
|
||||
static inline int sk_send_buffer_empty(sock *sk)
|
||||
{ return sk->tbuf == sk->tpos; }
|
||||
|
||||
static inline int sk_is_ipv4(sock *sk)
|
||||
{ return sk->af == AF_INET; }
|
||||
|
||||
static inline int sk_is_ipv6(sock *sk)
|
||||
{ return sk->af == AF_INET6; }
|
||||
|
||||
|
||||
|
||||
/* Socket flags */
|
||||
|
||||
#define SKF_V6ONLY 1 /* Use IPV6_V6ONLY socket option */
|
||||
#define SKF_LADDR_RX 2 /* Report local address for RX packets */
|
||||
#define SKF_LADDR_TX 4 /* Allow to specify local address for TX packets */
|
||||
#define SKF_V4ONLY 1 /* Use IPv4 for IP sockets */
|
||||
#define SKF_V6ONLY 2 /* Use IPV6_V6ONLY socket option */
|
||||
#define SKF_LADDR_RX 4 /* Report local address for RX packets */
|
||||
#define SKF_LADDR_TX 6 /* Allow to specify local address for TX packets */
|
||||
|
||||
|
||||
/*
|
||||
|
@ -17,6 +17,7 @@
|
||||
* if possible.
|
||||
*/
|
||||
|
||||
#include "lib/endian.h"
|
||||
#include "lib/string.h"
|
||||
|
||||
static inline u16
|
||||
|
@ -46,7 +46,7 @@ CF_KEYWORDS(ROUTER, ID, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OF
|
||||
CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE, STATES, ROUTES, FILTERS)
|
||||
CF_KEYWORDS(LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE)
|
||||
CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
|
||||
CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE, ROA, MAX, FLUSH)
|
||||
CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE, ROA, AS, MAX, FLUSH)
|
||||
CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION)
|
||||
CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC)
|
||||
|
||||
@ -82,14 +82,7 @@ rtrid: ROUTER ID idval ';' {
|
||||
|
||||
idval:
|
||||
NUM { $$ = $1; }
|
||||
| RTRID
|
||||
| IPA {
|
||||
#ifndef IPV6
|
||||
$$ = ipa_to_u32($1);
|
||||
#else
|
||||
cf_error("Router IDs must be entered as hexadecimal numbers or IPv4 addresses in IPv6 version");
|
||||
#endif
|
||||
}
|
||||
| IP4 { $$ = ip4_to_u32($1); }
|
||||
;
|
||||
|
||||
|
||||
|
@ -245,7 +245,7 @@ radv_sk_open(struct radv_iface *ifa)
|
||||
sk->saddr = ifa->addr->ip;
|
||||
|
||||
/* We want listen just to ICMPv6 messages of type RS and RA */
|
||||
if (sk_set_icmp_filter(sk, ICMPV6_RS, ICMPV6_RA) < 0)
|
||||
if (sk_set_icmp6_filter(sk, ICMPV6_RS, ICMPV6_RA) < 0)
|
||||
goto err;
|
||||
|
||||
if (sk_setup_multicast(sk) < 0)
|
||||
|
@ -26,8 +26,8 @@
|
||||
|
||||
#define ICMPV6_PROTO 58
|
||||
|
||||
#define AllNodes _MI(0xFF020000, 0, 0, 1) /* FF02::1 */
|
||||
#define AllRouters _MI(0xFF020000, 0, 0, 2) /* FF02::2 */
|
||||
#define AllNodes ipa_build6(0xFF020000, 0, 0, 1) /* FF02::1 */
|
||||
#define AllRouters ipa_build6(0xFF020000, 0, 0, 2) /* FF02::2 */
|
||||
|
||||
#define ICMPV6_RS 133
|
||||
#define ICMPV6_RA 134
|
||||
|
@ -718,7 +718,7 @@ new_iface(struct proto *p, struct iface *new, unsigned long flags, struct iface_
|
||||
#ifndef IPV6
|
||||
rif->sock->daddr = ipa_from_u32(0xe0000009);
|
||||
#else
|
||||
rif->sock->daddr = ipa_build(0xff020000, 0, 0, 9);
|
||||
rif->sock->daddr = ipa_build6(0xff020000, 0, 0, 9);
|
||||
#endif
|
||||
} else {
|
||||
rif->sock->daddr = new->addr->brd;
|
||||
@ -808,7 +808,7 @@ rip_if_notify(struct proto *p, unsigned c, struct iface *iface)
|
||||
#ifndef IPV6
|
||||
lock->addr = ipa_from_u32(0xe0000009);
|
||||
#else
|
||||
ip_pton("FF02::9", &lock->addr);
|
||||
lock->addr = ipa_build6(0xff020000, 0, 0, 9);
|
||||
#endif
|
||||
else
|
||||
lock->addr = iface->addr->brd;
|
||||
|
@ -9,6 +9,9 @@
|
||||
/* BIRD version */
|
||||
#define BIRD_VERSION "1.3.7"
|
||||
|
||||
// XXXX temporary define
|
||||
#define IPV1 1
|
||||
|
||||
/* Include parameters determined by configure script */
|
||||
#include "sysdep/autoconf.h"
|
||||
|
||||
|
@ -94,7 +94,7 @@ nl_send(struct nl_sock *nl, struct nlmsghdr *nh)
|
||||
}
|
||||
|
||||
static void
|
||||
nl_request_dump(int cmd)
|
||||
nl_request_dump(int pf, int cmd)
|
||||
{
|
||||
struct {
|
||||
struct nlmsghdr nh;
|
||||
@ -103,9 +103,7 @@ nl_request_dump(int cmd)
|
||||
req.nh.nlmsg_type = cmd;
|
||||
req.nh.nlmsg_len = sizeof(req);
|
||||
req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
|
||||
/* Is it important which PF_* is used for link-level interface scan?
|
||||
It seems that some information is available only when PF_INET is used. */
|
||||
req.g.rtgen_family = (cmd == RTM_GETLINK) ? PF_INET : BIRD_PF;
|
||||
req.g.rtgen_family = pf;
|
||||
nl_send(&nl_scan, &req.nh);
|
||||
}
|
||||
|
||||
@ -239,6 +237,19 @@ nl_parse_attrs(struct rtattr *a, struct rtattr **k, int ksize)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline ip4_addr rta4_get_ip4(struct rtattr *a)
|
||||
{ return ip4_get(RTA_DATA(a)); }
|
||||
|
||||
static inline ip6_addr rta6_get_ip6(struct rtattr *a)
|
||||
{ return ip6_get(RTA_DATA(a)); }
|
||||
|
||||
static inline ip_addr rta4_get_ipa(struct rtattr *a)
|
||||
{ return ipa_from_ip4(ip4_get(RTA_DATA(a))); }
|
||||
|
||||
static inline ip_addr rta6_get_ipa(struct rtattr *a)
|
||||
{ return ipa_from_ip6(ip6_get(RTA_DATA(a))); }
|
||||
|
||||
|
||||
void
|
||||
nl_add_attr(struct nlmsghdr *h, unsigned bufsize, unsigned code,
|
||||
void *data, unsigned dlen)
|
||||
@ -445,54 +456,58 @@ nl_parse_addr(struct nlmsghdr *h)
|
||||
{
|
||||
struct ifaddrmsg *i;
|
||||
struct rtattr *a[IFA_ANYCAST+1];
|
||||
int new = h->nlmsg_type == RTM_NEWADDR;
|
||||
int new = (h->nlmsg_type == RTM_NEWADDR);
|
||||
struct ifa ifa;
|
||||
struct iface *ifi;
|
||||
int scope;
|
||||
ip_addr addr;
|
||||
int ipv4 = 0;
|
||||
|
||||
if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(IFA_RTA(i), a, sizeof(a)))
|
||||
return;
|
||||
if (i->ifa_family != BIRD_AF)
|
||||
return;
|
||||
if (!a[IFA_ADDRESS] || RTA_PAYLOAD(a[IFA_ADDRESS]) != sizeof(ip_addr)
|
||||
#ifdef IPV6
|
||||
|| a[IFA_LOCAL] && RTA_PAYLOAD(a[IFA_LOCAL]) != sizeof(ip_addr)
|
||||
#else
|
||||
|| !a[IFA_LOCAL] || RTA_PAYLOAD(a[IFA_LOCAL]) != sizeof(ip_addr)
|
||||
|| (a[IFA_BROADCAST] && RTA_PAYLOAD(a[IFA_BROADCAST]) != sizeof(ip_addr))
|
||||
#endif
|
||||
)
|
||||
{
|
||||
log(L_ERR "nl_parse_addr: Malformed message received");
|
||||
return;
|
||||
}
|
||||
|
||||
ifi = if_find_by_index(i->ifa_index);
|
||||
if (!ifi)
|
||||
bzero(&ifa, sizeof(ifa));
|
||||
|
||||
if (i->ifa_family == AF_INET)
|
||||
{
|
||||
if (!a[IFA_ADDRESS] || (RTA_PAYLOAD(a[IFA_ADDRESS]) != sizeof(ip4_addr)) ||
|
||||
!a[IFA_LOCAL] || (RTA_PAYLOAD(a[IFA_LOCAL]) != sizeof(ip4_addr)))
|
||||
goto malformed;
|
||||
|
||||
addr = rta4_get_ipa(a[IFA_ADDRESS]);
|
||||
ifa.ip = rta4_get_ipa(a[IFA_LOCAL]);
|
||||
ipv4 = 1;
|
||||
}
|
||||
else if (i->ifa_family == AF_INET6)
|
||||
{
|
||||
if (!a[IFA_ADDRESS] || (RTA_PAYLOAD(a[IFA_ADDRESS]) != sizeof(ip6_addr)) ||
|
||||
(a[IFA_LOCAL] && (RTA_PAYLOAD(a[IFA_LOCAL]) != sizeof(ip6_addr))))
|
||||
goto malformed;
|
||||
|
||||
addr = rta6_get_ipa(a[IFA_ADDRESS]);
|
||||
/* IFA_LOCAL can be unset for IPv6 interfaces */
|
||||
ifa.ip = a[IFA_LOCAL] ? rta6_get_ipa(a[IFA_LOCAL]) : addr;
|
||||
}
|
||||
else
|
||||
return; /* Ignore unknown address families */
|
||||
|
||||
ifa.iface = if_find_by_index(i->ifa_index);
|
||||
if (!ifa.iface)
|
||||
{
|
||||
log(L_ERR "KIF: Received address message for unknown interface %d", i->ifa_index);
|
||||
return;
|
||||
}
|
||||
|
||||
bzero(&ifa, sizeof(ifa));
|
||||
ifa.iface = ifi;
|
||||
if (i->ifa_flags & IFA_F_SECONDARY)
|
||||
ifa.flags |= IA_SECONDARY;
|
||||
|
||||
/* IFA_LOCAL can be unset for IPv6 interfaces */
|
||||
memcpy(&ifa.ip, RTA_DATA(a[IFA_LOCAL] ? : a[IFA_ADDRESS]), sizeof(ifa.ip));
|
||||
ipa_ntoh(ifa.ip);
|
||||
ifa.pxlen = i->ifa_prefixlen;
|
||||
if (i->ifa_prefixlen > BITS_PER_IP_ADDRESS)
|
||||
ifa.pxlen = i->ifa_prefixlen + (ipv4 ? 96 : 0); // XXXXX: Hack
|
||||
if (ifa.pxlen > BITS_PER_IP_ADDRESS)
|
||||
{
|
||||
log(L_ERR "KIF: Invalid prefix length for interface %s: %d", ifi->name, i->ifa_prefixlen);
|
||||
new = 0;
|
||||
log(L_ERR "KIF: Received invalid pxlen %d on %s", i->ifa_prefixlen, ifa.iface->name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (i->ifa_prefixlen == BITS_PER_IP_ADDRESS)
|
||||
{
|
||||
ip_addr addr;
|
||||
memcpy(&addr, RTA_DATA(a[IFA_ADDRESS]), sizeof(addr));
|
||||
ipa_ntoh(addr);
|
||||
ifa.prefix = ifa.brd = addr;
|
||||
|
||||
/* It is either a host address or a peer address */
|
||||
@ -507,44 +522,36 @@ nl_parse_addr(struct nlmsghdr *h)
|
||||
else
|
||||
{
|
||||
ip_addr netmask = ipa_mkmask(ifa.pxlen);
|
||||
ifa.prefix = ipa_and(ifa.ip, netmask);
|
||||
ifa.brd = ipa_or(ifa.ip, ipa_not(netmask));
|
||||
ifa.prefix = ipa_and(addr, netmask);
|
||||
ifa.brd = ipa_or(addr, ipa_not(netmask));
|
||||
|
||||
if (i->ifa_prefixlen == BITS_PER_IP_ADDRESS - 1)
|
||||
ifa.opposite = ipa_opposite_m1(ifa.ip);
|
||||
|
||||
#ifndef IPV6
|
||||
if (i->ifa_prefixlen == BITS_PER_IP_ADDRESS - 2)
|
||||
if ((i->ifa_prefixlen == BITS_PER_IP_ADDRESS - 2) && ipv4)
|
||||
ifa.opposite = ipa_opposite_m2(ifa.ip);
|
||||
|
||||
if ((ifi->flags & IF_BROADCAST) && a[IFA_BROADCAST])
|
||||
{
|
||||
ip_addr xbrd;
|
||||
memcpy(&xbrd, RTA_DATA(a[IFA_BROADCAST]), sizeof(xbrd));
|
||||
ipa_ntoh(xbrd);
|
||||
if (ipa_equal(xbrd, ifa.prefix) || ipa_equal(xbrd, ifa.brd))
|
||||
ifa.brd = xbrd;
|
||||
else if (ifi->flags & IF_TMP_DOWN) /* Complain only during the first scan */
|
||||
log(L_ERR "KIF: Invalid broadcast address %I for %s", xbrd, ifi->name);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
scope = ipa_classify(ifa.ip);
|
||||
if (scope < 0)
|
||||
int scope = ipa_classify(ifa.ip);
|
||||
if ((scope == IADDR_INVALID) || !(scope & IADDR_HOST))
|
||||
{
|
||||
log(L_ERR "KIF: Invalid interface address %I for %s", ifa.ip, ifi->name);
|
||||
log(L_ERR "KIF: Received invalid address %I on %s", ifa.ip, ifa.iface->name);
|
||||
return;
|
||||
}
|
||||
ifa.scope = scope & IADDR_SCOPE_MASK;
|
||||
|
||||
DBG("KIF: IF%d(%s): %s IPA %I, flg %x, net %I/%d, brd %I, opp %I\n",
|
||||
ifi->index, ifi->name,
|
||||
new ? "added" : "removed",
|
||||
ifa.iface->index, ifa.iface->name, new ? "added" : "removed",
|
||||
ifa.ip, ifa.flags, ifa.prefix, ifa.pxlen, ifa.brd, ifa.opposite);
|
||||
if (new)
|
||||
ifa_update(&ifa);
|
||||
else
|
||||
ifa_delete(&ifa);
|
||||
return;
|
||||
|
||||
malformed:
|
||||
log(L_ERR "KIF: Received malformed address message");
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
@ -554,14 +561,24 @@ kif_do_scan(struct kif_proto *p UNUSED)
|
||||
|
||||
if_start_update();
|
||||
|
||||
nl_request_dump(RTM_GETLINK);
|
||||
/* Is it important which PF_* is used for link-level interface scan?
|
||||
It seems that some information is available only when PF_INET is used. */
|
||||
|
||||
nl_request_dump(PF_INET, RTM_GETLINK);
|
||||
while (h = nl_get_scan())
|
||||
if (h->nlmsg_type == RTM_NEWLINK || h->nlmsg_type == RTM_DELLINK)
|
||||
nl_parse_link(h, 1);
|
||||
else
|
||||
log(L_DEBUG "nl_scan_ifaces: Unknown packet received (type=%d)", h->nlmsg_type);
|
||||
|
||||
nl_request_dump(RTM_GETADDR);
|
||||
nl_request_dump(PF_INET, RTM_GETADDR);
|
||||
while (h = nl_get_scan())
|
||||
if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR)
|
||||
nl_parse_addr(h);
|
||||
else
|
||||
log(L_DEBUG "nl_scan_ifaces: Unknown packet received (type=%d)", h->nlmsg_type);
|
||||
|
||||
nl_request_dump(PF_INET6, RTM_GETADDR);
|
||||
while (h = nl_get_scan())
|
||||
if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR)
|
||||
nl_parse_addr(h);
|
||||
@ -944,7 +961,7 @@ krt_do_scan(struct krt_proto *p UNUSED) /* CONFIG_ALL_TABLES_AT_ONCE => p is NUL
|
||||
{
|
||||
struct nlmsghdr *h;
|
||||
|
||||
nl_request_dump(RTM_GETROUTE);
|
||||
nl_request_dump(BIRD_PF, RTM_GETROUTE);
|
||||
while (h = nl_get_scan())
|
||||
if (h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE)
|
||||
nl_parse_route(h, 1);
|
||||
@ -1055,11 +1072,9 @@ nl_open_async(void)
|
||||
|
||||
bzero(&sa, sizeof(sa));
|
||||
sa.nl_family = AF_NETLINK;
|
||||
#ifdef IPV6
|
||||
sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE;
|
||||
#else
|
||||
sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE;
|
||||
#endif
|
||||
sa.nl_groups = RTMGRP_LINK |
|
||||
RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE |
|
||||
RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE;
|
||||
if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0)
|
||||
{
|
||||
log(L_ERR "Unable to bind asynchronous rtnetlink socket: %m");
|
||||
|
@ -6,9 +6,11 @@
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#include <linux/socket.h>
|
||||
#include <linux/tcp.h>
|
||||
|
||||
#include <net/if.h>
|
||||
|
||||
#ifdef IPV6
|
||||
|
||||
#ifndef IPV6_UNICAST_HOPS
|
||||
/* Needed on glibc 2.0 systems */
|
||||
@ -16,22 +18,10 @@
|
||||
#define CONFIG_IPV6_GLIBC_20
|
||||
#endif
|
||||
|
||||
static inline void
|
||||
set_inaddr(struct in6_addr *ia, ip_addr a)
|
||||
{
|
||||
ipa_hton(a);
|
||||
memcpy(ia, &a, sizeof(a));
|
||||
}
|
||||
|
||||
static inline void
|
||||
get_inaddr(ip_addr *a, struct in6_addr *ia)
|
||||
{
|
||||
memcpy(a, ia, sizeof(*a));
|
||||
ipa_ntoh(*a);
|
||||
}
|
||||
|
||||
static inline char *
|
||||
sysio_bind_to_iface(sock *s)
|
||||
sk_bind_to_iface(sock *s)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
strcpy(ifr.ifr_name, s->iface->name);
|
||||
@ -41,22 +31,6 @@ sysio_bind_to_iface(sock *s)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline void
|
||||
set_inaddr(struct in_addr *ia, ip_addr a)
|
||||
{
|
||||
ipa_hton(a);
|
||||
memcpy(&ia->s_addr, &a, sizeof(a));
|
||||
}
|
||||
|
||||
static inline void
|
||||
get_inaddr(ip_addr *a, struct in_addr *ia)
|
||||
{
|
||||
memcpy(a, &ia->s_addr, sizeof(*a));
|
||||
ipa_ntoh(*a);
|
||||
}
|
||||
|
||||
|
||||
#ifndef HAVE_STRUCT_IP_MREQN
|
||||
/* Several versions of glibc don't define this structure, so we have to do it ourselves */
|
||||
@ -73,12 +47,12 @@ static inline void fill_mreqn(struct ip_mreqn *m, struct iface *ifa, ip_addr sad
|
||||
{
|
||||
bzero(m, sizeof(*m));
|
||||
m->imr_ifindex = ifa->index;
|
||||
set_inaddr(&m->imr_address, saddr);
|
||||
set_inaddr(&m->imr_multiaddr, maddr);
|
||||
ipa_put_in4(&m->imr_address, saddr);
|
||||
ipa_put_in4(&m->imr_multiaddr, maddr);
|
||||
}
|
||||
|
||||
static inline char *
|
||||
sysio_setup_multicast(sock *s)
|
||||
sk_setup_multicast4(sock *s)
|
||||
{
|
||||
struct ip_mreqn m;
|
||||
int zero = 0;
|
||||
@ -95,16 +69,11 @@ sysio_setup_multicast(sock *s)
|
||||
return "IP_MULTICAST_IF";
|
||||
|
||||
/* Is this necessary? */
|
||||
struct ifreq ifr;
|
||||
strcpy(ifr.ifr_name, s->iface->name);
|
||||
if (setsockopt(s->fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0)
|
||||
return "SO_BINDTODEVICE";
|
||||
|
||||
return NULL;
|
||||
return sk_bind_to_iface(s);
|
||||
}
|
||||
|
||||
static inline char *
|
||||
sysio_join_group(sock *s, ip_addr maddr)
|
||||
sk_join_group4(sock *s, ip_addr maddr)
|
||||
{
|
||||
struct ip_mreqn m;
|
||||
|
||||
@ -117,7 +86,7 @@ sysio_join_group(sock *s, ip_addr maddr)
|
||||
}
|
||||
|
||||
static inline char *
|
||||
sysio_leave_group(sock *s, ip_addr maddr)
|
||||
sk_leave_group4(sock *s, ip_addr maddr)
|
||||
{
|
||||
struct ip_mreqn m;
|
||||
|
||||
@ -129,12 +98,8 @@ sysio_leave_group(sock *s, ip_addr maddr)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#include <linux/socket.h>
|
||||
#include <linux/tcp.h>
|
||||
|
||||
/* For the case that we have older kernel headers */
|
||||
/* Copied from Linux kernel file include/linux/tcp.h */
|
||||
|
||||
@ -154,12 +119,12 @@ struct tcp_md5sig {
|
||||
#endif
|
||||
|
||||
static int
|
||||
sk_set_md5_auth_int(sock *s, sockaddr *sa, char *passwd)
|
||||
sk_set_md5_auth_int(sock *s, struct sockaddr *sa, int sa_len, char *passwd)
|
||||
{
|
||||
struct tcp_md5sig md5;
|
||||
|
||||
memset(&md5, 0, sizeof(md5));
|
||||
memcpy(&md5.tcpm_addr, (struct sockaddr *) sa, sizeof(*sa));
|
||||
memcpy(&md5.tcpm_addr, sa, sa_len);
|
||||
|
||||
if (passwd)
|
||||
{
|
||||
@ -182,57 +147,42 @@ sk_set_md5_auth_int(sock *s, sockaddr *sa, char *passwd)
|
||||
if (errno == ENOPROTOOPT)
|
||||
log(L_ERR "Kernel does not support TCP MD5 signatures");
|
||||
else
|
||||
log(L_ERR "sk_set_md5_auth_int: setsockopt: %m");
|
||||
log(L_ERR "sk_set_md5_auth_int: TCP_MD5SIG: %m");
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
#ifndef IPV6
|
||||
|
||||
/* RX/TX packet info handling for IPv4 */
|
||||
/* Mostly similar to standardized IPv6 code */
|
||||
|
||||
#define CMSG_RX_SPACE CMSG_SPACE(sizeof(struct in_pktinfo))
|
||||
#define CMSG_TX_SPACE CMSG_SPACE(sizeof(struct in_pktinfo))
|
||||
#define CMSG_SPACE_PKTINFO4 CMSG_SPACE(sizeof(struct in_pktinfo))
|
||||
#define CMSG_SPACE_PKTINFO6 CMSG_SPACE(sizeof(struct in6_pktinfo))
|
||||
|
||||
static char *
|
||||
sysio_register_cmsgs(sock *s)
|
||||
#define CMSG_RX_SPACE MAX(CMSG_SPACE_PKTINFO4,CMSG_SPACE_PKTINFO6)
|
||||
#define CMSG_TX_SPACE MAX(CMSG_SPACE_PKTINFO4,CMSG_SPACE_PKTINFO6)
|
||||
|
||||
static inline char *
|
||||
sk_request_pktinfo4(sock *s)
|
||||
{
|
||||
int ok = 1;
|
||||
if ((s->flags & SKF_LADDR_RX) &&
|
||||
setsockopt(s->fd, IPPROTO_IP, IP_PKTINFO, &ok, sizeof(ok)) < 0)
|
||||
return "IP_PKTINFO";
|
||||
if (s->flags & SKF_LADDR_RX)
|
||||
if (setsockopt(s->fd, IPPROTO_IP, IP_PKTINFO, &ok, sizeof(ok)) < 0)
|
||||
return "IP_PKTINFO";
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
sysio_process_rx_cmsgs(sock *s, struct msghdr *msg)
|
||||
static inline void
|
||||
sk_process_rx_cmsg4(sock *s, struct cmsghdr *cm)
|
||||
{
|
||||
struct cmsghdr *cm;
|
||||
struct in_pktinfo *pi = NULL;
|
||||
|
||||
if (!(s->flags & SKF_LADDR_RX))
|
||||
return;
|
||||
|
||||
for (cm = CMSG_FIRSTHDR(msg); cm != NULL; cm = CMSG_NXTHDR(msg, cm))
|
||||
if (cm->cmsg_level == IPPROTO_IP && cm->cmsg_type == IP_PKTINFO)
|
||||
{
|
||||
if (cm->cmsg_level == IPPROTO_IP && cm->cmsg_type == IP_PKTINFO)
|
||||
pi = (struct in_pktinfo *) CMSG_DATA(cm);
|
||||
struct in_pktinfo *pi = (struct in_pktinfo *) CMSG_DATA(cm);
|
||||
s->laddr = ipa_get_in4(&pi->ipi_addr);
|
||||
s->lifindex = pi->ipi_ifindex;
|
||||
}
|
||||
|
||||
if (!pi)
|
||||
{
|
||||
s->laddr = IPA_NONE;
|
||||
s->lifindex = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
get_inaddr(&s->laddr, &pi->ipi_addr);
|
||||
s->lifindex = pi->ipi_ifindex;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -261,8 +211,6 @@ sysio_prepare_tx_cmsgs(sock *s, struct msghdr *msg, void *cbuf, size_t cbuflen)
|
||||
}
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef IP_MINTTL
|
||||
#define IP_MINTTL 21
|
||||
@ -273,7 +221,8 @@ sysio_prepare_tx_cmsgs(sock *s, struct msghdr *msg, void *cbuf, size_t cbuflen)
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef IPV6
|
||||
// XXXX
|
||||
#if 0
|
||||
|
||||
static int
|
||||
sk_set_min_ttl4(sock *s, int ttl)
|
||||
@ -291,8 +240,6 @@ sk_set_min_ttl4(sock *s, int ttl)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int
|
||||
sk_set_min_ttl6(sock *s, int ttl)
|
||||
{
|
||||
|
553
sysdep/unix/io.c
553
sysdep/unix/io.c
@ -581,6 +581,77 @@ static struct resclass sk_class = {
|
||||
NULL
|
||||
};
|
||||
|
||||
#define SOCKADDR_DEFINE(sa, len, af) struct sockaddr *sa; int len; sockaddr_init(sa, len, af)
|
||||
#define sockaddr_init(sa, len, af) do { len=sockaddr_size(af); sa=alloca(len); sa->sa_family=af; } while(0)
|
||||
|
||||
static inline int sockaddr_size(int af)
|
||||
{ return (af == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); }
|
||||
|
||||
static inline void
|
||||
sockaddr_fill4(struct sockaddr_in *sa, ip_addr a, struct iface *ifa, unsigned port)
|
||||
{
|
||||
sa->sin_port = htons(port);
|
||||
#ifdef HAVE_SIN_LEN
|
||||
sa->sin_len = sizeof(struct sockaddr_in);
|
||||
#endif
|
||||
ipa_put_in4(&sa->sin_addr, a);
|
||||
}
|
||||
|
||||
static inline void
|
||||
sockaddr_fill6(struct sockaddr_in6 *sa, ip_addr a, struct iface *ifa, unsigned port)
|
||||
{
|
||||
sa->sin6_port = htons(port);
|
||||
sa->sin6_flowinfo = 0;
|
||||
#ifdef SIN6_LEN
|
||||
sa->sin6_len = sizeof(struct sockaddr_in6);
|
||||
#endif
|
||||
ipa_put_in6(&sa->sin6_addr, a);
|
||||
sa->sin6_scope_id = (ifa && ipa_is_link_local(a)) ? ifa->index : 0;
|
||||
}
|
||||
|
||||
void
|
||||
sockaddr_fill(struct sockaddr *sa, ip_addr a, struct iface *ifa, unsigned port)
|
||||
{
|
||||
if (sa->sa_family == AF_INET)
|
||||
sockaddr_fill4((struct sockaddr_in *) sa, a, ifa, port);
|
||||
else if (sa->sa_family == AF_INET6)
|
||||
sockaddr_fill6((struct sockaddr_in6 *) sa, a, ifa, port);
|
||||
else
|
||||
bug("%s called for wrong AF (%d)", "sockaddr_fill", sa->sa_family);
|
||||
}
|
||||
|
||||
static inline void
|
||||
sockaddr_read4(struct sockaddr_in *sa, ip_addr *a, struct iface **ifa, unsigned *port)
|
||||
{
|
||||
if (port)
|
||||
*port = ntohs(sa->sin_port);
|
||||
*a = ipa_get_in4(&sa->sin_addr);
|
||||
}
|
||||
|
||||
static inline void
|
||||
sockaddr_read6(struct sockaddr_in6 *sa, ip_addr *a, struct iface **ifa, unsigned *port)
|
||||
{
|
||||
if (port)
|
||||
*port = ntohs(sa->sin6_port);
|
||||
*a = ipa_get_in6(&sa->sin6_addr);
|
||||
|
||||
if (ifa && ipa_is_link_local(*a))
|
||||
*ifa = if_find_by_index(sa->sin6_scope_id);
|
||||
}
|
||||
|
||||
void
|
||||
sockaddr_read(struct sockaddr *sa, ip_addr *a, struct iface **ifa, unsigned *port, int check)
|
||||
{
|
||||
if (sa->sa_family == AF_INET)
|
||||
sockaddr_read4((struct sockaddr_in *) sa, a, ifa, port);
|
||||
else if (sa->sa_family == AF_INET6)
|
||||
sockaddr_read6((struct sockaddr_in6 *) sa, a, ifa, port);
|
||||
else if (check)
|
||||
bug("%s called for wrong AF (%d)", "sockaddr_read", sa->sa_family);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* sk_new - create a socket
|
||||
* @p: pool
|
||||
@ -607,71 +678,9 @@ sk_insert(sock *s)
|
||||
sock_recalc_fdsets_p = 1;
|
||||
}
|
||||
|
||||
#ifdef IPV6
|
||||
|
||||
void
|
||||
fill_in_sockaddr(struct sockaddr_in6 *sa, ip_addr a, struct iface *ifa, unsigned port)
|
||||
{
|
||||
memset(sa, 0, sizeof (struct sockaddr_in6));
|
||||
sa->sin6_family = AF_INET6;
|
||||
sa->sin6_port = htons(port);
|
||||
sa->sin6_flowinfo = 0;
|
||||
#ifdef HAVE_SIN_LEN
|
||||
sa->sin6_len = sizeof(struct sockaddr_in6);
|
||||
#endif
|
||||
set_inaddr(&sa->sin6_addr, a);
|
||||
|
||||
if (ifa && ipa_has_link_scope(a))
|
||||
sa->sin6_scope_id = ifa->index;
|
||||
}
|
||||
|
||||
void
|
||||
get_sockaddr(struct sockaddr_in6 *sa, ip_addr *a, struct iface **ifa, unsigned *port, int check)
|
||||
{
|
||||
if (check && sa->sin6_family != AF_INET6)
|
||||
bug("get_sockaddr called for wrong address family (%d)", sa->sin6_family);
|
||||
if (port)
|
||||
*port = ntohs(sa->sin6_port);
|
||||
memcpy(a, &sa->sin6_addr, sizeof(*a));
|
||||
ipa_ntoh(*a);
|
||||
|
||||
if (ifa && ipa_has_link_scope(*a))
|
||||
*ifa = if_find_by_index(sa->sin6_scope_id);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void
|
||||
fill_in_sockaddr(struct sockaddr_in *sa, ip_addr a, struct iface *ifa, unsigned port)
|
||||
{
|
||||
memset (sa, 0, sizeof (struct sockaddr_in));
|
||||
sa->sin_family = AF_INET;
|
||||
sa->sin_port = htons(port);
|
||||
#ifdef HAVE_SIN_LEN
|
||||
sa->sin_len = sizeof(struct sockaddr_in);
|
||||
#endif
|
||||
set_inaddr(&sa->sin_addr, a);
|
||||
}
|
||||
|
||||
void
|
||||
get_sockaddr(struct sockaddr_in *sa, ip_addr *a, struct iface **ifa, unsigned *port, int check)
|
||||
{
|
||||
if (check && sa->sin_family != AF_INET)
|
||||
bug("get_sockaddr called for wrong address family (%d)", sa->sin_family);
|
||||
if (port)
|
||||
*port = ntohs(sa->sin_port);
|
||||
memcpy(a, &sa->sin_addr.s_addr, sizeof(*a));
|
||||
ipa_ntoh(*a);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef IPV6
|
||||
|
||||
/* PKTINFO handling is also standardized in IPv6 */
|
||||
#define CMSG_RX_SPACE CMSG_SPACE(sizeof(struct in6_pktinfo))
|
||||
#define CMSG_TX_SPACE CMSG_SPACE(sizeof(struct in6_pktinfo))
|
||||
|
||||
/*
|
||||
* RFC 2292 uses IPV6_PKTINFO for both the socket option and the cmsg
|
||||
@ -683,42 +692,39 @@ get_sockaddr(struct sockaddr_in *sa, ip_addr *a, struct iface **ifa, unsigned *p
|
||||
#define IPV6_RECVPKTINFO IPV6_PKTINFO
|
||||
#endif
|
||||
|
||||
static char *
|
||||
sysio_register_cmsgs(sock *s)
|
||||
static inline char *
|
||||
sk_request_pktinfo6(sock *s)
|
||||
{
|
||||
int ok = 1;
|
||||
if ((s->flags & SKF_LADDR_RX) &&
|
||||
setsockopt(s->fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &ok, sizeof(ok)) < 0)
|
||||
return "IPV6_RECVPKTINFO";
|
||||
if (s->flags & SKF_LADDR_RX)
|
||||
if (setsockopt(s->fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &ok, sizeof(ok)) < 0)
|
||||
return "IPV6_RECVPKTINFO";
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
sysio_process_rx_cmsgs(sock *s, struct msghdr *msg)
|
||||
sk_process_rx_cmsgs(sock *s, struct msghdr *msg)
|
||||
{
|
||||
struct cmsghdr *cm;
|
||||
struct in6_pktinfo *pi = NULL;
|
||||
|
||||
if (!(s->flags & SKF_LADDR_RX))
|
||||
return;
|
||||
|
||||
s->laddr = IPA_NONE;
|
||||
s->lifindex = 0;
|
||||
|
||||
for (cm = CMSG_FIRSTHDR(msg); cm != NULL; cm = CMSG_NXTHDR(msg, cm))
|
||||
{
|
||||
if (cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_PKTINFO)
|
||||
pi = (struct in6_pktinfo *) CMSG_DATA(cm);
|
||||
}
|
||||
{
|
||||
struct in6_pktinfo *pi = (struct in6_pktinfo *) CMSG_DATA(cm);
|
||||
s->laddr = ipa_get_in6(&pi->ipi6_addr);
|
||||
s->lifindex = pi->ipi6_ifindex;
|
||||
}
|
||||
|
||||
if (!pi)
|
||||
{
|
||||
s->laddr = IPA_NONE;
|
||||
s->lifindex = 0;
|
||||
return;
|
||||
sk_process_rx_cmsg4(s, cm);
|
||||
}
|
||||
|
||||
get_inaddr(&s->laddr, &pi->ipi6_addr);
|
||||
s->lifindex = pi->ipi6_ifindex;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -747,25 +753,6 @@ sysio_prepare_tx_cmsgs(sock *s, struct msghdr *msg, void *cbuf, size_t cbuflen)
|
||||
return;
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
|
||||
static char *
|
||||
sk_set_ttl_int(sock *s)
|
||||
{
|
||||
#ifdef IPV6
|
||||
if (setsockopt(s->fd, SOL_IPV6, IPV6_UNICAST_HOPS, &s->ttl, sizeof(s->ttl)) < 0)
|
||||
return "IPV6_UNICAST_HOPS";
|
||||
#else
|
||||
if (setsockopt(s->fd, SOL_IP, IP_TTL, &s->ttl, sizeof(s->ttl)) < 0)
|
||||
return "IP_TTL";
|
||||
#ifdef CONFIG_UNIX_DONTROUTE
|
||||
int one = 1;
|
||||
if (s->ttl == 1 && setsockopt(s->fd, SOL_SOCKET, SO_DONTROUTE, &one, sizeof(one)) < 0)
|
||||
return "SO_DONTROUTE";
|
||||
#endif
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define ERR(x) do { err = x; goto bad; } while(0)
|
||||
#define WARN(x) log(L_WARN "sk_setup: %s: %m", x)
|
||||
@ -773,28 +760,32 @@ sk_set_ttl_int(sock *s)
|
||||
static char *
|
||||
sk_setup(sock *s)
|
||||
{
|
||||
int one = 1;
|
||||
int fd = s->fd;
|
||||
char *err = NULL;
|
||||
|
||||
if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
|
||||
ERR("fcntl(O_NONBLOCK)");
|
||||
if (s->type == SK_UNIX)
|
||||
|
||||
if (!s->af)
|
||||
return NULL;
|
||||
#ifndef IPV6
|
||||
if ((s->tos >= 0) && setsockopt(fd, SOL_IP, IP_TOS, &s->tos, sizeof(s->tos)) < 0)
|
||||
WARN("IP_TOS");
|
||||
#endif
|
||||
|
||||
#ifdef IPV6
|
||||
int v = 1;
|
||||
if ((s->flags & SKF_V6ONLY) && setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &v, sizeof(v)) < 0)
|
||||
WARN("IPV6_V6ONLY");
|
||||
#endif
|
||||
if (sk_is_ipv4(s) && (s->tos >= 0))
|
||||
if (setsockopt(fd, SOL_IP, IP_TOS, &s->tos, sizeof(s->tos)) < 0)
|
||||
WARN("IP_TOS");
|
||||
|
||||
if (sk_is_ipv6(s) && (s->flags & SKF_V6ONLY))
|
||||
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)) < 0)
|
||||
WARN("IPV6_V6ONLY");
|
||||
|
||||
// XXXX better error handling
|
||||
if (s->ttl >= 0)
|
||||
err = sk_set_ttl_int(s);
|
||||
sk_set_ttl(s, s->ttl);
|
||||
|
||||
sysio_register_cmsgs(s);
|
||||
if (sk_is_ipv4(s))
|
||||
err = sk_request_pktinfo4(s);
|
||||
else
|
||||
err = sk_request_pktinfo6(s);
|
||||
bad:
|
||||
return err;
|
||||
}
|
||||
@ -816,11 +807,30 @@ sk_set_ttl(sock *s, int ttl)
|
||||
{
|
||||
char *err;
|
||||
|
||||
s->ttl = ttl;
|
||||
if (err = sk_set_ttl_int(s))
|
||||
log(L_ERR "sk_set_ttl: %s: %m", err);
|
||||
if (sk_is_ipv4(s))
|
||||
{
|
||||
if (setsockopt(s->fd, SOL_IP, IP_TTL, &ttl, sizeof(ttl)) < 0)
|
||||
ERR("IP_TTL");
|
||||
|
||||
return (err ? -1 : 0);
|
||||
#ifdef CONFIG_UNIX_DONTROUTE
|
||||
int one = 1;
|
||||
if (ttl == 1)
|
||||
if (setsockopt(s->fd, SOL_SOCKET, SO_DONTROUTE, &one, sizeof(one)) < 0)
|
||||
ERR("SO_DONTROUTE");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
if (setsockopt(s->fd, SOL_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0)
|
||||
ERR("IPV6_UNICAST_HOPS");
|
||||
}
|
||||
|
||||
s->ttl = ttl;
|
||||
return 0;
|
||||
|
||||
bad:
|
||||
log(L_ERR "sk_set_ttl: %s: %m", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -836,16 +846,30 @@ sk_set_ttl(sock *s, int ttl)
|
||||
int
|
||||
sk_set_min_ttl(sock *s, int ttl)
|
||||
{
|
||||
int err;
|
||||
#ifdef IPV6
|
||||
err = sk_set_min_ttl6(s, ttl);
|
||||
#else
|
||||
err = sk_set_min_ttl4(s, ttl);
|
||||
#endif
|
||||
char *err;
|
||||
|
||||
return err;
|
||||
if (sk_is_ipv4(s))
|
||||
{
|
||||
if (setsockopt(s->fd, IPPROTO_IP, IP_MINTTL, &ttl, sizeof(ttl)) < 0)
|
||||
ERR("IP_MINTTL");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (setsockopt(s->fd, IPPROTO_IPV6, IPV6_MINHOPCOUNT, &ttl, sizeof(ttl)) < 0)
|
||||
ERR("IPV6_MINHOPCOUNT");
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
bad:
|
||||
if (errno == ENOPROTOOPT)
|
||||
log(L_ERR "Kernel does not support %s TTL security", sk_is_ipv4(s) ? "IPv4" : "IPv6");
|
||||
else
|
||||
log(L_ERR "sk_set_min_ttl: %s: %m", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* sk_set_md5_auth - add / remove MD5 security association for given socket.
|
||||
* @s: socket
|
||||
@ -868,9 +892,9 @@ sk_set_min_ttl(sock *s, int ttl)
|
||||
int
|
||||
sk_set_md5_auth(sock *s, ip_addr a, struct iface *ifa, char *passwd)
|
||||
{
|
||||
sockaddr sa;
|
||||
fill_in_sockaddr(&sa, a, ifa, 0);
|
||||
return sk_set_md5_auth_int(s, &sa, passwd);
|
||||
SOCKADDR_DEFINE(sa, sa_len, s->af);
|
||||
sockaddr_fill(sa, a, ifa, 0);
|
||||
return sk_set_md5_auth_int(s, sa, sa_len, passwd);
|
||||
}
|
||||
|
||||
int
|
||||
@ -885,9 +909,6 @@ sk_set_broadcast(sock *s, int enable)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef IPV6
|
||||
|
||||
int
|
||||
sk_set_ipv6_checksum(sock *s, int offset)
|
||||
{
|
||||
@ -901,7 +922,7 @@ sk_set_ipv6_checksum(sock *s, int offset)
|
||||
}
|
||||
|
||||
int
|
||||
sk_set_icmp_filter(sock *s, int p1, int p2)
|
||||
sk_set_icmp6_filter(sock *s, int p1, int p2)
|
||||
{
|
||||
/* a bit of lame interface, but it is here only for Radv */
|
||||
struct icmp6_filter f;
|
||||
@ -912,86 +933,45 @@ sk_set_icmp_filter(sock *s, int p1, int p2)
|
||||
|
||||
if (setsockopt(s->fd, IPPROTO_ICMPV6, ICMP6_FILTER, &f, sizeof(f)) < 0)
|
||||
{
|
||||
log(L_ERR "sk_setup_icmp_filter: ICMP6_FILTER: %m");
|
||||
log(L_ERR "sk_set_icmp6_filter: ICMP6_FILTER: %m");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sk_setup_multicast(sock *s)
|
||||
|
||||
static inline void
|
||||
fill_mreq6(struct ipv6_mreq *m, struct iface *ifa, ip_addr maddr)
|
||||
{
|
||||
bzero(m, sizeof(*m));
|
||||
|
||||
#ifdef CONFIG_IPV6_GLIBC_20
|
||||
m->ipv6mr_ifindex = ifa->index;
|
||||
#else
|
||||
m->ipv6mr_interface = ifa->index;
|
||||
#endif
|
||||
|
||||
ipa_put_in6(&m->ipv6mr_multiaddr, maddr);
|
||||
}
|
||||
|
||||
static inline char *
|
||||
sk_setup_multicast6(sock *s)
|
||||
{
|
||||
char *err;
|
||||
int zero = 0;
|
||||
int index;
|
||||
int index = s->iface->index;
|
||||
|
||||
ASSERT(s->iface && s->iface->addr);
|
||||
|
||||
index = s->iface->index;
|
||||
if (setsockopt(s->fd, SOL_IPV6, IPV6_MULTICAST_HOPS, &s->ttl, sizeof(s->ttl)) < 0)
|
||||
ERR("IPV6_MULTICAST_HOPS");
|
||||
return "IPV6_MULTICAST_HOPS";
|
||||
if (setsockopt(s->fd, SOL_IPV6, IPV6_MULTICAST_LOOP, &zero, sizeof(zero)) < 0)
|
||||
ERR("IPV6_MULTICAST_LOOP");
|
||||
return "IPV6_MULTICAST_LOOP";
|
||||
if (setsockopt(s->fd, SOL_IPV6, IPV6_MULTICAST_IF, &index, sizeof(index)) < 0)
|
||||
ERR("IPV6_MULTICAST_IF");
|
||||
return "IPV6_MULTICAST_IF";
|
||||
|
||||
if (err = sysio_bind_to_iface(s))
|
||||
goto bad;
|
||||
|
||||
return 0;
|
||||
|
||||
bad:
|
||||
log(L_ERR "sk_setup_multicast: %s: %m", err);
|
||||
return -1;
|
||||
/* Is this necessary? */
|
||||
return sk_bind_to_iface(s);
|
||||
}
|
||||
|
||||
int
|
||||
sk_join_group(sock *s, ip_addr maddr)
|
||||
{
|
||||
struct ipv6_mreq mreq;
|
||||
|
||||
set_inaddr(&mreq.ipv6mr_multiaddr, maddr);
|
||||
|
||||
#ifdef CONFIG_IPV6_GLIBC_20
|
||||
mreq.ipv6mr_ifindex = s->iface->index;
|
||||
#else
|
||||
mreq.ipv6mr_interface = s->iface->index;
|
||||
#endif
|
||||
|
||||
if (setsockopt(s->fd, SOL_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) < 0)
|
||||
{
|
||||
log(L_ERR "sk_join_group: IPV6_JOIN_GROUP: %m");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sk_leave_group(sock *s, ip_addr maddr)
|
||||
{
|
||||
struct ipv6_mreq mreq;
|
||||
|
||||
set_inaddr(&mreq.ipv6mr_multiaddr, maddr);
|
||||
|
||||
#ifdef CONFIG_IPV6_GLIBC_20
|
||||
mreq.ipv6mr_ifindex = s->iface->index;
|
||||
#else
|
||||
mreq.ipv6mr_interface = s->iface->index;
|
||||
#endif
|
||||
|
||||
if (setsockopt(s->fd, SOL_IPV6, IPV6_LEAVE_GROUP, &mreq, sizeof(mreq)) < 0)
|
||||
{
|
||||
log(L_ERR "sk_leave_group: IPV6_LEAVE_GROUP: %m");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else /* IPV4 */
|
||||
|
||||
int
|
||||
sk_setup_multicast(sock *s)
|
||||
{
|
||||
@ -999,7 +979,12 @@ sk_setup_multicast(sock *s)
|
||||
|
||||
ASSERT(s->iface && s->iface->addr);
|
||||
|
||||
if (err = sysio_setup_multicast(s))
|
||||
if (sk_is_ipv4(s))
|
||||
err = sk_setup_multicast4(s);
|
||||
else
|
||||
err = sk_setup_multicast6(s);
|
||||
|
||||
if (err)
|
||||
{
|
||||
log(L_ERR "sk_setup_multicast: %s: %m", err);
|
||||
return -1;
|
||||
@ -1008,12 +993,29 @@ sk_setup_multicast(sock *s)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline char *
|
||||
sk_join_group6(sock *s, ip_addr maddr)
|
||||
{
|
||||
struct ipv6_mreq m;
|
||||
|
||||
fill_mreq6(&m, s->iface, maddr);
|
||||
if (setsockopt(s->fd, SOL_IPV6, IPV6_JOIN_GROUP, &m, sizeof(m)) < 0)
|
||||
return "IPV6_JOIN_GROUP";
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
sk_join_group(sock *s, ip_addr maddr)
|
||||
{
|
||||
char *err;
|
||||
char *err;
|
||||
|
||||
if (err = sysio_join_group(s, maddr))
|
||||
if (sk_is_ipv4(s))
|
||||
err = sk_join_group4(s, maddr);
|
||||
else
|
||||
err = sk_join_group6(s, maddr);
|
||||
|
||||
if (err)
|
||||
{
|
||||
log(L_ERR "sk_join_group: %s: %m", err);
|
||||
return -1;
|
||||
@ -1022,12 +1024,29 @@ sk_join_group(sock *s, ip_addr maddr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline char *
|
||||
sk_leave_group6(sock *s, ip_addr maddr)
|
||||
{
|
||||
struct ipv6_mreq m;
|
||||
|
||||
fill_mreq6(&m, s->iface, maddr);
|
||||
if (setsockopt(s->fd, SOL_IPV6, IPV6_LEAVE_GROUP, &m, sizeof(m)) < 0)
|
||||
return "IPV6_LEAVE_GROUP";
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
sk_leave_group(sock *s, ip_addr maddr)
|
||||
{
|
||||
char *err;
|
||||
char *err;
|
||||
|
||||
if (err = sysio_leave_group(s, maddr))
|
||||
if (sk_is_ipv4(s))
|
||||
err = sk_leave_group4(s, maddr);
|
||||
else
|
||||
err = sk_leave_group6(s, maddr);
|
||||
|
||||
if (err)
|
||||
{
|
||||
log(L_ERR "sk_leave_group: %s: %m", err);
|
||||
return -1;
|
||||
@ -1036,16 +1055,15 @@ sk_leave_group(sock *s, ip_addr maddr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static void
|
||||
sk_tcp_connected(sock *s)
|
||||
{
|
||||
sockaddr lsa;
|
||||
int lsa_len = sizeof(lsa);
|
||||
if (getsockname(s->fd, (struct sockaddr *) &lsa, &lsa_len) == 0)
|
||||
get_sockaddr(&lsa, &s->saddr, &s->iface, &s->sport, 1);
|
||||
SOCKADDR_DEFINE(sa, sa_len, s->af);
|
||||
|
||||
if (getsockname(s->fd, sa, &sa_len) == 0)
|
||||
sockaddr_read(sa, &s->saddr, &s->iface, &s->sport, 1);
|
||||
|
||||
s->type = SK_TCP;
|
||||
sk_alloc_bufs(s);
|
||||
@ -1053,35 +1071,35 @@ sk_tcp_connected(sock *s)
|
||||
}
|
||||
|
||||
static int
|
||||
sk_passive_connected(sock *s, struct sockaddr *sa, int al, int type)
|
||||
sk_passive_connected(sock *s, struct sockaddr *sa, int sa_len, int type)
|
||||
{
|
||||
int fd = accept(s->fd, sa, &al);
|
||||
int fd = accept(s->fd, sa, &sa_len);
|
||||
if (fd >= 0)
|
||||
{
|
||||
sock *t = sk_new(s->pool);
|
||||
char *err;
|
||||
t->type = type;
|
||||
t->fd = fd;
|
||||
t->af = s->af;
|
||||
t->ttl = s->ttl;
|
||||
t->tos = s->tos;
|
||||
t->rbsize = s->rbsize;
|
||||
t->tbsize = s->tbsize;
|
||||
if (type == SK_TCP)
|
||||
{
|
||||
sockaddr lsa;
|
||||
int lsa_len = sizeof(lsa);
|
||||
if (getsockname(fd, (struct sockaddr *) &lsa, &lsa_len) == 0)
|
||||
get_sockaddr(&lsa, &t->saddr, &t->iface, &t->sport, 1);
|
||||
SOCKADDR_DEFINE(lsa, lsa_len, t->af);
|
||||
if (getsockname(fd, lsa, &lsa_len) == 0) // XXXX
|
||||
sockaddr_read(lsa, &t->saddr, &t->iface, &t->sport, 1);
|
||||
|
||||
get_sockaddr((sockaddr *) sa, &t->daddr, &t->iface, &t->dport, 1);
|
||||
sockaddr_read(sa, &t->daddr, &t->iface, &t->dport, 1);
|
||||
}
|
||||
sk_insert(t);
|
||||
if (err = sk_setup(t))
|
||||
{
|
||||
log(L_ERR "Incoming connection: %s: %m", err);
|
||||
log(L_ERR "sk_passive_connected: %s: %m", err);
|
||||
rfree(t);
|
||||
return 1;
|
||||
}
|
||||
sk_insert(t);
|
||||
sk_alloc_bufs(t);
|
||||
s->rx_hook(t, 0);
|
||||
return 1;
|
||||
@ -1106,9 +1124,6 @@ sk_passive_connected(sock *s, struct sockaddr *sa, int al, int type)
|
||||
int
|
||||
sk_open(sock *s)
|
||||
{
|
||||
int fd;
|
||||
sockaddr sa;
|
||||
int one = 1;
|
||||
int type = s->type;
|
||||
int has_src = ipa_nonzero(s->saddr) || s->sport;
|
||||
char *err;
|
||||
@ -1119,27 +1134,35 @@ sk_open(sock *s)
|
||||
s->ttx = ""; /* Force s->ttx != s->tpos */
|
||||
/* Fall thru */
|
||||
case SK_TCP_PASSIVE:
|
||||
fd = socket(BIRD_PF, SOCK_STREAM, IPPROTO_TCP);
|
||||
s->af = (s->flags & SKF_V4ONLY) ? AF_INET : AF_INET6;
|
||||
s->fd = socket(s->af, SOCK_STREAM, IPPROTO_TCP);
|
||||
break;
|
||||
case SK_UDP:
|
||||
fd = socket(BIRD_PF, SOCK_DGRAM, IPPROTO_UDP);
|
||||
s->af = (s->flags & SKF_V4ONLY) ? AF_INET : AF_INET6;
|
||||
s->fd = socket(s->af, SOCK_DGRAM, IPPROTO_UDP);
|
||||
break;
|
||||
case SK_IP:
|
||||
fd = socket(BIRD_PF, SOCK_RAW, s->dport);
|
||||
s->af = (s->flags & SKF_V4ONLY) ? AF_INET : AF_INET6;
|
||||
s->fd = socket(s->af, SOCK_RAW, s->dport);
|
||||
break;
|
||||
case SK_MAGIC:
|
||||
fd = s->fd;
|
||||
break;
|
||||
if (err = sk_setup(s))
|
||||
goto bad;
|
||||
sk_insert(s);
|
||||
return 0;
|
||||
default:
|
||||
bug("sk_open() called for invalid sock type %d", type);
|
||||
}
|
||||
|
||||
int fd = s->fd;
|
||||
if (fd < 0)
|
||||
die("sk_open: socket: %m");
|
||||
s->fd = fd;
|
||||
ERR("socket");
|
||||
|
||||
if (err = sk_setup(s))
|
||||
goto bad;
|
||||
|
||||
SOCKADDR_DEFINE(sa, sa_len, s->af);
|
||||
|
||||
if (has_src)
|
||||
{
|
||||
int port;
|
||||
@ -1149,26 +1172,26 @@ sk_open(sock *s)
|
||||
else
|
||||
{
|
||||
port = s->sport;
|
||||
|
||||
int one = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
|
||||
ERR("SO_REUSEADDR");
|
||||
}
|
||||
fill_in_sockaddr(&sa, s->saddr, s->iface, port);
|
||||
if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0)
|
||||
|
||||
sockaddr_fill(sa, s->saddr, s->iface, port);
|
||||
if (bind(fd, sa, sa_len) < 0)
|
||||
ERR("bind");
|
||||
}
|
||||
fill_in_sockaddr(&sa, s->daddr, s->iface, s->dport);
|
||||
sockaddr_fill(sa, s->daddr, s->iface, s->dport);
|
||||
|
||||
if (s->password)
|
||||
{
|
||||
int rv = sk_set_md5_auth_int(s, &sa, s->password);
|
||||
if (rv < 0)
|
||||
goto bad_no_log;
|
||||
}
|
||||
if (sk_set_md5_auth_int(s, sa, sa_len, s->password) < 0)
|
||||
goto bad_no_log;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case SK_TCP_ACTIVE:
|
||||
if (connect(fd, (struct sockaddr *) &sa, sizeof(sa)) >= 0)
|
||||
if (connect(fd, sa, sa_len) >= 0)
|
||||
sk_tcp_connected(s);
|
||||
else if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS &&
|
||||
errno != ECONNREFUSED && errno != EHOSTUNREACH && errno != ENETUNREACH)
|
||||
@ -1178,26 +1201,25 @@ sk_open(sock *s)
|
||||
if (listen(fd, 8))
|
||||
ERR("listen");
|
||||
break;
|
||||
case SK_MAGIC:
|
||||
break;
|
||||
default:
|
||||
sk_alloc_bufs(s);
|
||||
#ifdef IPV6
|
||||
#ifdef IPV6_MTU_DISCOVER
|
||||
{
|
||||
int dont = IPV6_PMTUDISC_DONT;
|
||||
if (setsockopt(fd, SOL_IPV6, IPV6_MTU_DISCOVER, &dont, sizeof(dont)) < 0)
|
||||
ERR("IPV6_MTU_DISCOVER");
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
|
||||
#ifdef IP_PMTUDISC
|
||||
{
|
||||
int dont = IP_PMTUDISC_DONT;
|
||||
if (setsockopt(fd, SOL_IP, IP_PMTUDISC, &dont, sizeof(dont)) < 0)
|
||||
ERR("IP_PMTUDISC");
|
||||
}
|
||||
if (sk_is_ipv4(s))
|
||||
{
|
||||
int dont = IP_PMTUDISC_DONT;
|
||||
if (setsockopt(fd, SOL_IP, IP_PMTUDISC, &dont, sizeof(dont)) < 0)
|
||||
ERR("IP_PMTUDISC");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef IPV6_MTU_DISCOVER
|
||||
if (sk_is_ipv6(s))
|
||||
{
|
||||
int dont = IPV6_PMTUDISC_DONT;
|
||||
if (setsockopt(fd, SOL_IPV6, IPV6_MTU_DISCOVER, &dont, sizeof(dont)) < 0)
|
||||
ERR("IPV6_MTU_DISCOVER");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -1207,7 +1229,8 @@ sk_open(sock *s)
|
||||
bad:
|
||||
log(L_ERR "sk_open: %s: %m", err);
|
||||
bad_no_log:
|
||||
close(fd);
|
||||
close(s->fd);
|
||||
s->af = 0;
|
||||
s->fd = -1;
|
||||
return -1;
|
||||
}
|
||||
@ -1278,15 +1301,15 @@ sk_maybe_write(sock *s)
|
||||
if (s->tbuf == s->tpos)
|
||||
return 1;
|
||||
|
||||
sockaddr sa;
|
||||
fill_in_sockaddr(&sa, s->daddr, s->iface, s->dport);
|
||||
SOCKADDR_DEFINE(sa, sa_len, s->af);
|
||||
sockaddr_fill(sa, s->daddr, s->iface, s->dport);
|
||||
|
||||
struct iovec iov = {s->tbuf, s->tpos - s->tbuf};
|
||||
// byte cmsg_buf[CMSG_TX_SPACE];
|
||||
|
||||
struct msghdr msg = {
|
||||
.msg_name = &sa,
|
||||
.msg_namelen = sizeof(sa),
|
||||
.msg_name = sa,
|
||||
.msg_namelen = sa_len,
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1};
|
||||
|
||||
@ -1396,13 +1419,12 @@ sk_read(sock *s)
|
||||
{
|
||||
case SK_TCP_PASSIVE:
|
||||
{
|
||||
sockaddr sa;
|
||||
return sk_passive_connected(s, (struct sockaddr *) &sa, sizeof(sa), SK_TCP);
|
||||
SOCKADDR_DEFINE(sa, sa_len, s->af);
|
||||
return sk_passive_connected(s, sa, sa_len, SK_TCP);
|
||||
}
|
||||
case SK_UNIX_PASSIVE:
|
||||
{
|
||||
struct sockaddr_un sa;
|
||||
return sk_passive_connected(s, (struct sockaddr *) &sa, sizeof(sa), SK_UNIX);
|
||||
return sk_passive_connected(s, NULL, 0, SK_UNIX);
|
||||
}
|
||||
case SK_TCP:
|
||||
case SK_UNIX:
|
||||
@ -1433,15 +1455,15 @@ sk_read(sock *s)
|
||||
return s->rx_hook(s, 0);
|
||||
default:
|
||||
{
|
||||
sockaddr sa;
|
||||
SOCKADDR_DEFINE(sa, sa_len, s->af);
|
||||
int e;
|
||||
|
||||
struct iovec iov = {s->rbuf, s->rbsize};
|
||||
byte cmsg_buf[CMSG_RX_SPACE];
|
||||
|
||||
struct msghdr msg = {
|
||||
.msg_name = &sa,
|
||||
.msg_namelen = sizeof(sa),
|
||||
.msg_name = sa,
|
||||
.msg_namelen = sa_len,
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = cmsg_buf,
|
||||
@ -1457,8 +1479,8 @@ sk_read(sock *s)
|
||||
return 0;
|
||||
}
|
||||
s->rpos = s->rbuf + e;
|
||||
get_sockaddr(&sa, &s->faddr, NULL, &s->fport, 1);
|
||||
sysio_process_rx_cmsgs(s, &msg);
|
||||
sockaddr_read(sa, &s->faddr, NULL, &s->fport, 1);
|
||||
sk_process_rx_cmsgs(s, &msg);
|
||||
|
||||
s->rx_hook(s, e);
|
||||
return 1;
|
||||
@ -1473,9 +1495,10 @@ sk_write(sock *s)
|
||||
{
|
||||
case SK_TCP_ACTIVE:
|
||||
{
|
||||
sockaddr sa;
|
||||
fill_in_sockaddr(&sa, s->daddr, s->iface, s->dport);
|
||||
if (connect(s->fd, (struct sockaddr *) &sa, sizeof(sa)) >= 0 || errno == EISCONN)
|
||||
SOCKADDR_DEFINE(sa, sa_len, s->af);
|
||||
sockaddr_fill(sa, s->daddr, s->iface, s->dport);
|
||||
|
||||
if (connect(s->fd, sa, sa_len) >= 0 || errno == EISCONN)
|
||||
sk_tcp_connected(s);
|
||||
else if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS)
|
||||
s->err_hook(s, errno);
|
||||
|
@ -28,17 +28,11 @@ volatile int async_config_flag;
|
||||
volatile int async_dump_flag;
|
||||
volatile int async_shutdown_flag;
|
||||
|
||||
#ifdef IPV6
|
||||
// XXXX
|
||||
#define BIRD_PF PF_INET6
|
||||
#define BIRD_AF AF_INET6
|
||||
typedef struct sockaddr_in6 sockaddr;
|
||||
static inline int sa_family_check(sockaddr *sa) { return sa->sin6_family == AF_INET6; }
|
||||
#else
|
||||
#define BIRD_PF PF_INET
|
||||
#define BIRD_AF AF_INET
|
||||
typedef struct sockaddr_in sockaddr;
|
||||
static inline int sa_family_check(sockaddr *sa) { return sa->sin_family == AF_INET; }
|
||||
#endif
|
||||
static inline int sa_family_check(struct sockaddr *sa) { return sa->sa_family == AF_INET6; }
|
||||
|
||||
|
||||
#ifndef SUN_LEN
|
||||
#define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) + strlen ((ptr)->sun_path))
|
||||
@ -49,8 +43,6 @@ struct iface;
|
||||
|
||||
void io_init(void);
|
||||
void io_loop(void);
|
||||
void fill_in_sockaddr(sockaddr *sa, ip_addr a, struct iface *ifa, unsigned port);
|
||||
void get_sockaddr(sockaddr *sa, ip_addr *a, struct iface **ifa, unsigned *port, int check);
|
||||
void sk_open_unix(struct birdsock *s, char *name);
|
||||
void *tracked_fopen(struct pool *, char *name, char *mode);
|
||||
void test_old_bird(char *path);
|
||||
|
@ -57,14 +57,14 @@ tags:
|
||||
|
||||
install: all
|
||||
$(INSTALL) -d $(DESTDIR)/$(sbindir) $(DESTDIR)/$(sysconfdir) $(DESTDIR)/@runtimedir@
|
||||
$(INSTALL_PROGRAM) -s $(exedir)/bird $(DESTDIR)/$(sbindir)/bird@SUFFIX@
|
||||
$(INSTALL_PROGRAM) -s $(exedir)/bird $(DESTDIR)/$(sbindir)/bird
|
||||
if test -n "@CLIENT@" ; then \
|
||||
$(INSTALL_PROGRAM) -s $(exedir)/birdc $(DESTDIR)/$(sbindir)/birdc@SUFFIX@ ; \
|
||||
$(INSTALL_PROGRAM) -s $(exedir)/birdc $(DESTDIR)/$(sbindir)/birdc ; \
|
||||
fi
|
||||
if ! test -f $(DESTDIR)/@CONFIG_FILE@ ; then \
|
||||
$(INSTALL_DATA) $(srcdir)/doc/bird.conf.example $(DESTDIR)/@CONFIG_FILE@ ; \
|
||||
else \
|
||||
echo "Not overwriting old bird@SUFFIX@.conf" ; \
|
||||
echo "Not overwriting old bird.conf" ; \
|
||||
fi
|
||||
|
||||
install-docs:
|
||||
|
Loading…
Reference in New Issue
Block a user