From dce267832a0468ed5e596f0b0733b926af7ead3a Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Tue, 3 Aug 1999 19:36:06 +0000 Subject: [PATCH] Basic support for IPv6. The system-dependent part doesn't work yet, but the core routines are there and seem to be working. o lib/ipv6.[ch] written o Lexical analyser recognizes IPv6 addresses and when in IPv6 mode, treats pure IPv4 addresses as router IDs. o Router ID must be configured manually on IPv6 systems. o Added SCOPE_ORGANIZATION for org-scoped IPv6 multicasts. o Fixed few places where ipa_(hton|ntoh) was called as a function returning converted address. --- TODO | 19 +- conf/cf-lex.l | 18 +- conf/conf.c | 4 + conf/confbase.Y | 2 + lib/Modules | 2 +- lib/ip.h | 3 +- lib/ipv6.c | 349 ++++++++++++++++++++++++++++++++- lib/ipv6.h | 46 +++-- nest/config.Y | 13 +- nest/iface.c | 4 +- sysdep/cf/linux-v6.h | 9 +- sysdep/linux/netlink/netlink.c | 13 +- sysdep/unix/io.c | 4 +- 13 files changed, 438 insertions(+), 48 deletions(-) diff --git a/TODO b/TODO index d4f55e58..802c2d07 100644 --- a/TODO +++ b/TODO @@ -14,8 +14,7 @@ Core - default preferences of protocols: prefer BGP over OSPF/RIP external routes? - static: check validity of route destination? - -- device: configuration of interface patterns +- static: allow specifying a per-route filter program for setting route attributes? - rte_update: check whether all bits not covered by masklen are zero - rte_update: debug mode @@ -31,6 +30,7 @@ Core - config: executable config files - config: when parsing prefix, check zero bits - config: reconfiguration +- config: useless rules when protocols disabled - krt: rescan interfaces when route addition fails? - krt: does PERSIST mode have any sense if kernel syncer is shut down as last? @@ -53,47 +53,41 @@ Cleanup - replace all NUM, IPA and expr tokens by constant filter expressions - try compiling with -Wunused - does everybody test return value of sk_open? +- add references to RFC's we did follow Various ideas ~~~~~~~~~~~~~ - real multipath (doesn't seem to be simple at all :() - fake multipath (even less simple) - route recalculation timing and flap dampening [see RFC2439 for algorithms] -- aggregate engine: standard route aggregation and summarization +- aggregate engine: standard route aggregation and summarization [RFC2519] - aggregate engine: injection of manually configured pseudo-static routes - generate default route if any working BGP connection exists (aggregate engine again?) - generate default route to IGP's (aggregate engine yet another time?) +- look at RFC 2386 (QoS-based routing) RIP ~~~ - - RIP: export-only and import-only mode? + - export-only and import-only mode? - drop RIPv1 (Historic protocol)? - Route Tag - limit routing table xfer (frequency, only to neighbors) - - multicast on/off (per interface) OSPF ~~~~ - importing of device routes for networks where we don't run OSPF - check incoming packets using neighbor cache - - stub area: either no external routes or only default route - RFC2328 appendix E: Use a better algorithm - automatic generation of external route tags (RFC1403) - RFC1587 NSSA areas - RFC2370 opaque LSA's - - RFC1793 Demand Circuit Support ?? - respect interface MTU and try not to create larger packets unless unavoidable - BGP ~~~ - - in, local, out RIB - - maxsize=4096 - - detection of loops - aggregation, ATOMIC_AGGREGATE - communities - - confederations - attributes must be sorted! - re-export of NEXT_HOP attribute - BGP session over currently down interface @@ -104,7 +98,6 @@ BGP - inter-advertisement delay???! - normalize (sort) incoming AS-SET's - maximum length of AS paths - - expected neighbor AS - hold time - idle timer after error: initial value, exponential growth, maximum value diff --git a/conf/cf-lex.l b/conf/cf-lex.l index 94c2b2ab..21727a9d 100644 --- a/conf/cf-lex.l +++ b/conf/cf-lex.l @@ -65,9 +65,25 @@ WHITE [ \t] %% {DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+ { +#ifdef IPV6 + if (ipv4_pton_u32(yytext, &cf_lval.i32)) + return RTRID; + cf_error("Invalid IPv4 address %s", yytext); +#else if (ip_pton(yytext, &cf_lval.a)) return IPA; - cf_error("Invalid IP address"); + 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 } 0x{DIGIT}+ { diff --git a/conf/conf.c b/conf/conf.c index 0ce593c7..d9bf9d88 100644 --- a/conf/conf.c +++ b/conf/conf.c @@ -54,6 +54,10 @@ config_parse(struct config *c) cf_parse(); filters_postconfig(); /* FIXME: Do we really need this? */ protos_postconfig(c); +#ifdef IPV6 + if (!c->router_id) + cf_error("Router ID must be configured manually on IPv6 routers"); +#endif return 1; } diff --git a/conf/confbase.Y b/conf/confbase.Y index f9293a40..a6eb8764 100644 --- a/conf/confbase.Y +++ b/conf/confbase.Y @@ -22,6 +22,7 @@ CF_DECLS %union { int i; + u32 i32; ip_addr a; struct symbol *s; char *t; @@ -35,6 +36,7 @@ CF_DECLS %token END %token NUM +%token RTRID %token IPA %token SYM %token TEXT diff --git a/lib/Modules b/lib/Modules index 1597b9af..cf654127 100644 --- a/lib/Modules +++ b/lib/Modules @@ -2,7 +2,7 @@ birdlib.h bitops.c bitops.h ip.h -#ifdef CONFIG_IPV6 +#ifdef IPV6 ipv6.c ipv6.h #else diff --git a/lib/ip.h b/lib/ip.h index 1bce89f8..9b247d57 100644 --- a/lib/ip.h +++ b/lib/ip.h @@ -35,7 +35,8 @@ #define SCOPE_HOST 0 #define SCOPE_LINK 1 #define SCOPE_SITE 2 -#define SCOPE_UNIVERSE 3 +#define SCOPE_ORGANIZATION 3 +#define SCOPE_UNIVERSE 4 /* * Is it a valid network prefix? diff --git a/lib/ipv6.c b/lib/ipv6.c index e612af72..c1b07637 100644 --- a/lib/ipv6.c +++ b/lib/ipv6.c @@ -1,12 +1,357 @@ /* * BIRD Library -- IPv6 Address Manipulation Functions * - * (c) 1998 Martin Mares + * (c) 1999 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL. + * */ +#include +#include +#include + #include "nest/bird.h" #include "lib/ip.h" +#include "lib/bitops.h" +#include "lib/endian.h" -#error "Ought to implement these." +/* + * 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]; + + /* FIXME: Relax these requirements? */ + if ((x & 0xe0000000) == 0x20000000) /* Aggregatable Global Unicast Address */ + return IADDR_HOST | SCOPE_UNIVERSE; + if ((x & 0xfc000000) == 0xe8000000) /* Link-Local Address */ + return IADDR_HOST | SCOPE_LINK; + if ((x & 0xfc000000) == 0xec000000) /* Site-Local Address */ + return IADDR_HOST | SCOPE_SITE; + if ((x & 0xff000000) == 0xff000000) /* 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; + } + } + return IADDR_INVALID; +} + +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; + + 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; + char *c; + + /* 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 += sprintf(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 += sprintf(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 += sprintf(b, "%08x", a.addr[i]); + } + return b; +} + +int +ipv4_pton_u32(char *a, u32 *o) +{ + int i,j; + 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; +} + +#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 diff --git a/lib/ipv6.h b/lib/ipv6.h index 35d8e29d..59005f94 100644 --- a/lib/ipv6.h +++ b/lib/ipv6.h @@ -1,7 +1,7 @@ /* * BIRD -- IP Addresses et Cetera for IPv6 * - * (c) 1998 Martin Mares + * (c) 1999 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -12,11 +12,11 @@ #include #include -typedef struct ipv4_addr { +typedef struct ipv6_addr { u32 addr[4]; } ip_addr; -#define _MI(a,b,c,d) ((struct ip_addr) { a, b, c, d }) +#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]) @@ -28,38 +28,50 @@ typedef struct ipv4_addr { #define IPA_NONE _MI(0,0,0,0) #define ipa_equal(x,y) (!memcmp(&(x),&(y),sizeof(ip_addr))) -#define ipa_nonzero(x) (_I0(a) || _I1(a) || _I2(a) || _I3(a)) -#define ipa_and(a,b) _MI(_I0(a) & _I0(b), \ - _I1(a) & _I1(b), \ - _I2(a) & _I2(b), \ - _I3(a) & _I3(b)) -#define ipa_or(a,b) _MI(_I0(a) | _I0(b), \ - _I1(a) | _I1(b), \ - _I2(a) | _I2(b), \ - _I3(a) | _I3(b)) -#define ipa_xor(a,b) _MI(_I0(a) ^ _I0(b), \ - _I1(a) ^ _I1(b), \ - _I2(a) ^ _I2(b), \ - _I3(a) ^ _I3(b)) -#define ipa_not(a) _MI(~_I0(a),~_I1(a),~_I2(a),~_I3(a)) +#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)) +/* ipa_opposite and 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) 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 *); /* FIXME: Is this hash function uniformly distributed over standard 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; } +#define IP_PREC_INTERNET_CONTROL 0 /* FIXME: What's the right value? */ + #endif diff --git a/nest/config.Y b/nest/config.Y index 0c653211..dfef4e27 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -20,7 +20,7 @@ CF_KEYWORDS(ROUTER, ID, PROTOCOL, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT) CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE) CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID) -%type idval +%type idval %type imexport %type rtable %type

password_list password_begin @@ -37,8 +37,15 @@ rtrid: ROUTER ID idval ';' { ; idval: - NUM - | IPA { $$ = ipa_to_u32($1); } + NUM { $$ = $1; } + | RTRID + | IPA { +#ifndef IPV6 + $$ = ipa_to_u32($1); +#else + cf_error("Router IDs must be entered as hexadecimal numbers in IPv6 version"); +#endif + } ; /* Creation of routing tables */ diff --git a/nest/iface.c b/nest/iface.c index 3a5d9c6b..debec45d 100644 --- a/nest/iface.c +++ b/nest/iface.c @@ -549,8 +549,9 @@ ifa_delete(struct ifa *a) } static void -auto_router_id(void) /* FIXME: What if we run IPv6??? */ +auto_router_id(void) { +#ifndef IPV6 struct iface *i, *j; j = NULL; @@ -564,6 +565,7 @@ auto_router_id(void) /* FIXME: What if we run IPv6??? */ die("Cannot determine router ID (no suitable network interface found), please configure it manually"); debug("Guessed router ID %I (%s)\n", j->addr->ip, j->name); config->router_id = ipa_to_u32(j->addr->ip); +#endif } void diff --git a/sysdep/cf/linux-v6.h b/sysdep/cf/linux-v6.h index 69ab0f8f..ef52ee46 100644 --- a/sysdep/cf/linux-v6.h +++ b/sysdep/cf/linux-v6.h @@ -8,9 +8,16 @@ #define IPV6 -#define CONFIG_TOS #define CONFIG_AUTO_ROUTES #define CONFIG_ALL_MULTICAST +#define CONFIG_SELF_CONSCIOUS + +/* + * Netlink supports multiple tables, but kernel IPv6 code doesn't, so we + * treat it as a multiple table system with number of tables set to 1. + */ +#define CONFIG_MULTIPLE_TABLES +#define CONFIG_ALL_TABLES_AT_ONCE /* Link: sysdep/linux/netlink diff --git a/sysdep/linux/netlink/netlink.c b/sysdep/linux/netlink/netlink.c index 6682dbc2..f71f1f5e 100644 --- a/sysdep/linux/netlink/netlink.c +++ b/sysdep/linux/netlink/netlink.c @@ -242,7 +242,7 @@ nl_add_attr_ipa(struct nlmsghdr *h, unsigned maxsize, int code, ip_addr ipa) a = (struct rtattr *)((char *)h + NLMSG_ALIGN(h->nlmsg_len)); a->rta_type = code; a->rta_len = len; - ipa = ipa_hton(ipa); + ipa_hton(ipa); memcpy(RTA_DATA(a), &ipa, sizeof(ipa)); h->nlmsg_len = NLMSG_ALIGN(h->nlmsg_len) + len; } @@ -351,17 +351,18 @@ nl_parse_addr(struct nlmsghdr *h) if (i->ifa_flags & IFA_F_SECONDARY) ifa.flags |= IA_SECONDARY; memcpy(&ifa.ip, RTA_DATA(a[IFA_LOCAL]), sizeof(ifa.ip)); - ifa.ip = ipa_ntoh(ifa.ip); + ipa_ntoh(ifa.ip); ifa.pxlen = i->ifa_prefixlen; if (ifi->flags & IF_UNNUMBERED) { memcpy(&ifa.opposite, RTA_DATA(a[IFA_ADDRESS]), sizeof(ifa.opposite)); - ifa.opposite = ifa.brd = ipa_ntoh(ifa.opposite); + ipa_ntoh(ifa.opposite); + ifa.brd = ifa.opposite; } else if ((ifi->flags & IF_BROADCAST) && a[IFA_BROADCAST]) { memcpy(&ifa.brd, RTA_DATA(a[IFA_BROADCAST]), sizeof(ifa.brd)); - ifa.brd = ipa_ntoh(ifa.brd); + ipa_ntoh(ifa.brd); } /* else a NBMA link */ ifa.prefix = ipa_and(ifa.ip, ipa_mkmask(ifa.pxlen)); @@ -575,7 +576,7 @@ nl_parse_route(struct nlmsghdr *h, int scan) if (a[RTA_DST]) { memcpy(&dst, RTA_DATA(a[RTA_DST]), sizeof(dst)); - dst = ipa_ntoh(dst); + ipa_ntoh(dst); } else dst = IPA_NONE; @@ -630,7 +631,7 @@ nl_parse_route(struct nlmsghdr *h, int scan) neighbor *ng; ra.dest = RTD_ROUTER; memcpy(&ra.gw, RTA_DATA(a[RTA_GATEWAY]), sizeof(ra.gw)); - ra.gw = ipa_ntoh(ra.gw); + ipa_ntoh(ra.gw); ng = neigh_find(&p->p, &ra.gw, 0); if (ng) ra.iface = ng->iface; diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 39cf200f..3e070fa1 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -314,7 +314,7 @@ sk_new(pool *p) static inline void set_inaddr(struct in_addr *ia, ip_addr a) { - a = ipa_hton(a); + ipa_hton(a); memcpy(&ia->s_addr, &a, sizeof(a)); } @@ -334,7 +334,7 @@ get_sockaddr(struct sockaddr_in *sa, ip_addr *a, unsigned *port) if (port) *port = ntohs(sa->sin_port); memcpy(a, &sa->sin_addr.s_addr, sizeof(*a)); - *a = ipa_ntoh(*a); + ipa_ntoh(*a); } static char *