From: Benjamin Cama Date: Fri, 29 Jul 2011 11:04:31 +0000 (+0200) Subject: Merge branch 'rfc4760' into fdn-mods X-Git-Tag: debian/2.2.1-1fdn1~1^2 X-Git-Url: http://git.sameswireless.fr/l2tpns.git/commitdiff_plain/1c898f7017d2f3bfc07dfdadd077d70ccf6066c1?ds=sidebyside;hp=-c Merge branch 'rfc4760' into fdn-mods --- 1c898f7017d2f3bfc07dfdadd077d70ccf6066c1 diff --combined bgp.c index 8e722de,4a4bea0..600c950 --- a/bgp.c +++ b/bgp.c @@@ -32,8 -32,11 +32,11 @@@ static void bgp_set_retry(struct bgp_pe static void bgp_cidr(in_addr_t ip, in_addr_t mask, struct bgp_ip_prefix *pfx); static struct bgp_route_list *bgp_insert_route(struct bgp_route_list *head, struct bgp_route_list *new); + static struct bgp_route6_list *bgp_insert_route6(struct bgp_route6_list *head, + struct bgp_route6_list *new); static void bgp_free_routes(struct bgp_route_list *routes); + static void bgp_free_routes6(struct bgp_route6_list *routes); static char const *bgp_msg_type_str(uint8_t type); static int bgp_connect(struct bgp_peer *peer); static int bgp_handle_connect(struct bgp_peer *peer); @@@ -43,11 -46,13 +46,13 @@@ static int bgp_handle_input(struct bgp_ static int bgp_send_open(struct bgp_peer *peer); static int bgp_send_keepalive(struct bgp_peer *peer); static int bgp_send_update(struct bgp_peer *peer); + static int bgp_send_update6(struct bgp_peer *peer); static int bgp_send_notification(struct bgp_peer *peer, uint8_t code, uint8_t subcode); static uint16_t our_as; static struct bgp_route_list *bgp_routes = 0; + static struct bgp_route6_list *bgp_routes6 = 0; int bgp_configured = 0; struct bgp_peer *bgp_peers = 0; @@@ -88,6 -93,7 +93,7 @@@ int bgp_setup(int as return 0; bgp_routes = 0; + bgp_routes6 = 0; bgp_configured = 0; /* set by bgp_start */ return 1; @@@ -95,7 -101,7 +101,7 @@@ /* start connection with a peer */ int bgp_start(struct bgp_peer *peer, char *name, int as, int keepalive, - int hold, int enable) + int hold, struct in_addr update_source, int enable) { struct hostent *h; int ibgp; @@@ -124,7 -130,6 +130,7 @@@ } memcpy(&peer->addr, h->h_addr, sizeof(peer->addr)); + peer->source_addr = update_source.s_addr; peer->as = as > 0 ? as : our_as; ibgp = peer->as == our_as; @@@ -192,22 -197,6 +198,6 @@@ ADD_ATTRIBUTE(); - /* NEXT_HOP */ - a.flags = BGP_PATH_ATTR_FLAG_TRANS; - a.code = BGP_PATH_ATTR_CODE_NEXT_HOP; - if (config->nexthop_address) - { - ip = config->nexthop_address; - } - else - { - ip = my_address; /* we're it */ - } - a.data.s.len = sizeof(ip); - memcpy(a.data.s.value, &ip, sizeof(ip)); - - ADD_ATTRIBUTE(); - /* MULTI_EXIT_DISC */ a.flags = BGP_PATH_ATTR_FLAG_OPTIONAL; a.code = BGP_PATH_ATTR_CODE_MULTI_EXIT_DISC; @@@ -237,6 -226,18 +227,25 @@@ ADD_ATTRIBUTE(); + /* remember the len before adding NEXT_HOP */ + peer->path_attr_len_without_nexthop = peer->path_attr_len; + + /* NEXT_HOP */ + a.flags = BGP_PATH_ATTR_FLAG_TRANS; + a.code = BGP_PATH_ATTR_CODE_NEXT_HOP; - ip = my_address; /* we're it */ ++ if (config->nexthop_address) ++ { ++ ip = config->nexthop_address; ++ } ++ else ++ { ++ ip = my_address; /* we're it */ ++ } + a.data.s.len = sizeof(ip); + memcpy(a.data.s.value, &ip, sizeof(ip)); + + ADD_ATTRIBUTE(); + if (!(peer->path_attrs = malloc(peer->path_attr_len))) { LOG(0, 0, 0, "Can't allocate path_attrs for %s (%s)\n", @@@ -247,6 -248,53 +256,53 @@@ memcpy(peer->path_attrs, path_attrs, peer->path_attr_len); + /* multiprotocol attributes initialization */ + if (config->ipv6_prefix.s6_addr[0]) + { + struct bgp_attr_mp_reach_nlri_partial mp_reach_nlri_partial; + struct bgp_attr_mp_unreach_nlri_partial mp_unreach_nlri_partial; + + a.flags = BGP_PATH_ATTR_FLAG_OPTIONAL; + a.code = BGP_PATH_ATTR_CODE_MP_REACH_NLRI; + a.data.s.len = 0; /* will be set on UPDATE */ + + mp_reach_nlri_partial.afi = htons(BGP_MP_AFI_IPv6); + mp_reach_nlri_partial.safi = BGP_MP_SAFI_UNICAST; + mp_reach_nlri_partial.reserved = 0; + mp_reach_nlri_partial.next_hop_len = 16; + + /* use the defined nexthop6, or our address in ipv6_prefix */ + if (config->nexthop6_address.s6_addr[0]) + memcpy(&mp_reach_nlri_partial.next_hop, + &config->nexthop6_address.s6_addr, 16); + else + { + /* our address is ipv6prefix::1 */ + memcpy(&mp_reach_nlri_partial.next_hop, + &config->ipv6_prefix.s6_addr, 16); + mp_reach_nlri_partial.next_hop[15] = 1; + } + + memcpy(&a.data.s.value, &mp_reach_nlri_partial, + sizeof(struct bgp_attr_mp_reach_nlri_partial)); + memcpy(&peer->mp_reach_nlri_partial, &a, + BGP_PATH_ATTR_MP_REACH_NLRI_PARTIAL_SIZE); + + a.flags = BGP_PATH_ATTR_FLAG_OPTIONAL | BGP_PATH_ATTR_FLAG_EXTLEN; + a.code = BGP_PATH_ATTR_CODE_MP_UNREACH_NLRI; + a.data.e.len = 0; /* will be set on UPDATE */ + + mp_unreach_nlri_partial.afi = htons(BGP_MP_AFI_IPv6); + mp_unreach_nlri_partial.safi = BGP_MP_SAFI_UNICAST; + + memcpy(&a.data.e.value, &mp_unreach_nlri_partial, + sizeof(struct bgp_attr_mp_unreach_nlri_partial)); + memcpy(&peer->mp_unreach_nlri_partial, &a, + BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE); + } + + peer->mp_handling = HandlingUnknown; + LOG(4, 0, 0, "Initiating BGP connection to %s (routing %s)\n", name, enable ? "enabled" : "suspended"); @@@ -275,6 -323,8 +331,8 @@@ static void bgp_clear(struct bgp_peer * bgp_free_routes(peer->routes); peer->routes = 0; + bgp_free_routes6(peer->routes6); + peer->routes6 = 0; peer->outbuf->packet.header.len = 0; peer->outbuf->done = 0; @@@ -382,6 -432,33 +440,33 @@@ static struct bgp_route_list *bgp_inser return head; } + /* insert route6 into list; sorted */ + static struct bgp_route6_list *bgp_insert_route6(struct bgp_route6_list *head, + struct bgp_route6_list *new) + { + struct bgp_route6_list *p = head; + struct bgp_route6_list *e = 0; + + while (p && memcmp(&p->dest, &new->dest, sizeof(p->dest)) < 0) + { + e = p; + p = p->next; + } + + if (e) + { + new->next = e->next; + e->next = new; + } + else + { + new->next = head; + head = new; + } + + return head; + } + /* add route to list for peers */ /* * Note: this doesn't do route aggregation, nor drop routes if a less @@@ -434,6 -511,59 +519,59 @@@ int bgp_add_route(in_addr_t ip, in_addr return 1; } + /* add route to list for peers */ + /* + * Note: same provisions as above + */ + int bgp_add_route6(struct in6_addr ip, int prefixlen) + { + struct bgp_route6_list *r = bgp_routes6; + struct bgp_route6_list add; + int i; + char ipv6addr[INET6_ADDRSTRLEN]; + + memcpy(&add.dest.prefix, &ip.s6_addr, 16); + add.dest.len = prefixlen; + add.next = 0; + + /* check for duplicate */ + while (r) + { + i = memcmp(&r->dest, &add.dest, sizeof(r->dest)); + if (!i) + return 1; /* already covered */ + + if (i > 0) + break; + + r = r->next; + } + + /* insert into route list; sorted */ + if (!(r = malloc(sizeof(*r)))) + { + LOG(0, 0, 0, "Can't allocate route for %s/%d (%s)\n", + inet_ntop(AF_INET6, &ip, ipv6addr, INET6_ADDRSTRLEN), add.dest.len, + strerror(errno)); + + return 0; + } + + memcpy(r, &add, sizeof(*r)); + bgp_routes6 = bgp_insert_route6(bgp_routes6, r); + + /* flag established peers for update */ + for (i = 0; i < BGP_NUM_PEERS; i++) + if (bgp_peers[i].state == Established + && bgp_peers[i].mp_handling == HandleIPv6Routes) + bgp_peers[i].update_routes6 = 1; + + LOG(4, 0, 0, "Registered BGP route %s/%d\n", + inet_ntop(AF_INET6, &ip, ipv6addr, INET6_ADDRSTRLEN), add.dest.len); + + return 1; + } + /* remove route from list for peers */ int bgp_del_route(in_addr_t ip, in_addr_t mask) { @@@ -483,6 -613,58 +621,58 @@@ return 1; } + /* remove route from list for peers */ + int bgp_del_route6(struct in6_addr ip, int prefixlen) + { + struct bgp_route6_list *r = bgp_routes6; + struct bgp_route6_list *e = 0; + struct bgp_route6_list del; + int i; + char ipv6addr[INET6_ADDRSTRLEN]; + + memcpy(&del.dest.prefix, &ip.s6_addr, 16); + del.dest.len = prefixlen; + del.next = 0; + + /* find entry in routes list and remove */ + while (r) + { + i = memcmp(&r->dest, &del.dest, sizeof(r->dest)); + if (!i) + { + if (e) + e->next = r->next; + else + bgp_routes6 = r->next; + + free(r); + break; + } + + e = r; + + if (i > 0) + r = 0; /* stop */ + else + r = r->next; + } + + /* not found */ + if (!r) + return 1; + + /* flag established peers for update */ + for (i = 0; i < BGP_NUM_PEERS; i++) + if (bgp_peers[i].state == Established + && bgp_peers[i].mp_handling == HandleIPv6Routes) + bgp_peers[i].update_routes6 = 1; + + LOG(4, 0, 0, "Removed BGP route %s/%d\n", + inet_ntop(AF_INET6, &ip, ipv6addr, INET6_ADDRSTRLEN), del.dest.len); + + return 1; + } + /* enable or disable routing */ void bgp_enable_routing(int enable) { @@@ -627,6 -809,14 +817,14 @@@ int bgp_process(uint32_t events[] continue; } + /* process pending IPv6 updates */ + if (peer->update_routes6 + && !peer->outbuf->packet.header.len) /* ditto */ + { + if (!bgp_send_update6(peer)) + continue; + } + /* process timers */ if (peer->state == Established) { @@@ -670,6 -860,17 +868,17 @@@ static void bgp_free_routes(struct bgp_ } } + static void bgp_free_routes6(struct bgp_route6_list *routes) + { + struct bgp_route6_list *tmp; + + while ((tmp = routes)) + { + routes = tmp->next; + free(tmp); + } + } + char const *bgp_state_str(enum bgp_state state) { switch (state) @@@ -704,7 -905,6 +913,7 @@@ static int bgp_connect(struct bgp_peer { static int bgp_port = 0; struct sockaddr_in addr; + struct sockaddr_in source_addr; struct epoll_event ev; if (!bgp_port) @@@ -736,19 -936,6 +945,19 @@@ /* set to non-blocking */ fcntl(peer->sock, F_SETFL, fcntl(peer->sock, F_GETFL, 0) | O_NONBLOCK); + /* set source address */ + memset(&source_addr, 0, sizeof(source_addr)); + source_addr.sin_family = AF_INET; + source_addr.sin_addr.s_addr = peer->source_addr; /* defaults to INADDR_ANY */ + if (bind(peer->sock, (struct sockaddr *) &source_addr, sizeof(source_addr)) < 0) + { + LOG(1, 0, 0, "Can't set source address to %s: %s\n", + inet_ntoa(source_addr.sin_addr), strerror(errno)); + + bgp_set_retry(peer); + return 0; + } + /* try connect */ memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; @@@ -924,6 -1111,12 +1133,12 @@@ static int bgp_handle_input(struct bgp_ struct bgp_data_open data; int hold; int i; + off_t param_offset, capability_offset; + struct bgp_opt_param *param; + uint8_t capabilities_len; + char *capabilities = NULL; + struct bgp_capability *capability; + struct bgp_mp_cap_param *mp_cap; for (i = 0; i < sizeof(p->header.marker); i++) { @@@ -986,10 -1179,93 +1201,93 @@@ if (peer->keepalive * 3 > peer->hold) peer->keepalive = peer->hold / 3; + /* check for optional parameters */ + /* 2 is for the size of type + len (both uint8_t) */ + for (param_offset = 0; + param_offset < data.opt_len; + param_offset += 2 + param->len) + { + param = (struct bgp_opt_param *)((char *)&data.opt_params + param_offset); + + /* sensible check */ + if (data.opt_len - param_offset < 2 + || param->len > data.opt_len - param_offset - 2) + { + LOG(1, 0, 0, "Malformed Optional Parameter list from BGP peer %s\n", + peer->name); + + bgp_send_notification(peer, BGP_ERR_OPEN, BGP_ERR_UNSPEC); + return 0; + } + + /* we know only one parameter type */ + if (param->type != BGP_PARAM_TYPE_CAPABILITY) + { + LOG(1, 0, 0, "Unsupported Optional Parameter type %d from BGP peer %s\n", + param->type, peer->name); + + bgp_send_notification(peer, BGP_ERR_OPEN, BGP_ERR_OPN_UNSUP_PARAM); + return 0; + } + + capabilities_len = param->len; + capabilities = (char *)¶m->value; + + /* look for BGP multiprotocol capability */ + for (capability_offset = 0; + capability_offset < capabilities_len; + capability_offset += 2 + capability->len) + { + capability = (struct bgp_capability *)(capabilities + capability_offset); + + /* sensible check */ + if (capabilities_len - capability_offset < 2 + || capability->len > capabilities_len - capability_offset - 2) + { + LOG(1, 0, 0, "Malformed Capabilities list from BGP peer %s\n", + peer->name); + + bgp_send_notification(peer, BGP_ERR_OPEN, BGP_ERR_UNSPEC); + return 0; + } + + /* we only know one capability code */ + if (capability->code != BGP_CAP_CODE_MP + && capability->len != sizeof(struct bgp_mp_cap_param)) + { + LOG(4, 0, 0, "Unsupported Capability code %d from BGP peer %s\n", + capability->code, peer->name); + + /* we don't terminate, still; we just jump to the next one */ + continue; + } + + mp_cap = (struct bgp_mp_cap_param *)&capability->value; + /* the only tuple we support */ + if (ntohs(mp_cap->afi) != BGP_MP_AFI_IPv6 && mp_cap->safi != BGP_MP_SAFI_UNICAST) + { + LOG(4, 0, 0, "Unsupported multiprotocol AFI %d and SAFI %d from BGP peer %s\n", + mp_cap->afi, mp_cap->safi, peer->name); + + /* we don't terminate, still; we just jump to the next one */ + continue; + } + + /* yes it can! */ + peer->mp_handling = HandleIPv6Routes; + } + } + + if (peer->mp_handling != HandleIPv6Routes) + { + peer->mp_handling = DoesntHandleIPv6Routes; + if (config->ipv6_prefix.s6_addr[0]) + LOG(1, 0, 0, "Warning: BGP peer %s doesn't handle IPv6 prefixes updates\n", + peer->name); + } + /* next transition requires an exchange of keepalives */ bgp_send_keepalive(peer); - - /* FIXME: may need to check for optional params */ } break; @@@ -1018,10 -1294,30 +1316,30 @@@ if (notification->error_code == BGP_ERR_CEASE) { LOG(4, 0, 0, "BGP peer %s sent CEASE\n", peer->name); - bgp_restart(peer); + bgp_set_retry(peer); return 0; } + if (notification->error_code == BGP_ERR_OPEN + && notification->error_subcode == BGP_ERR_OPN_UNSUP_PARAM) + { + LOG(4, 0, 0, "BGP peer %s doesn't support BGP Capabilities\n", peer->name); + peer->mp_handling = DoesntHandleIPv6Routes; + bgp_set_retry(peer); + return 0; + } + + if (notification->error_code == BGP_ERR_OPEN + && notification->error_subcode == BGP_ERR_OPN_UNSUP_CAP) + { + /* the only capability we advertise is this one, so upon receiving + an "unsupported capability" message, we disable IPv6 routes for + this peer */ + LOG(4, 0, 0, "BGP peer %s doesn't support IPv6 routes advertisement\n", peer->name); + peer->mp_handling = DoesntHandleIPv6Routes; + break; + } + /* FIXME: should handle more notifications */ LOG(4, 0, 0, "BGP peer %s sent unhandled NOTIFICATION %d\n", peer->name, (int) notification->error_code); @@@ -1052,6 -1348,9 +1370,9 @@@ static int bgp_send_open(struct bgp_peer *peer) { struct bgp_data_open data; + struct bgp_mp_cap_param mp_ipv6 = { htons(BGP_MP_AFI_IPv6), 0, BGP_MP_SAFI_UNICAST }; + struct bgp_capability cap_mp_ipv6; + struct bgp_opt_param param_cap_mp_ipv6; uint16_t len = sizeof(peer->outbuf->packet.header); memset(peer->outbuf->packet.header.marker, 0xff, @@@ -1063,10 -1362,30 +1384,30 @@@ data.as = htons(our_as); data.hold_time = htons(peer->hold); data.identifier = my_address; - data.opt_len = 0; - memcpy(peer->outbuf->packet.data, &data, BGP_DATA_OPEN_SIZE); - len += BGP_DATA_OPEN_SIZE; + /* if we know peer doesn't support MP (mp_handling == DoesntHandleIPv6Routes) + then don't add this parameter */ + if (config->ipv6_prefix.s6_addr[0] + && (peer->mp_handling == HandlingUnknown + || peer->mp_handling == HandleIPv6Routes)) + { + /* construct the param and capability */ + cap_mp_ipv6.code = BGP_CAP_CODE_MP; + cap_mp_ipv6.len = sizeof(mp_ipv6); + memcpy(&cap_mp_ipv6.value, &mp_ipv6, cap_mp_ipv6.len); + + param_cap_mp_ipv6.type = BGP_PARAM_TYPE_CAPABILITY; + param_cap_mp_ipv6.len = 2 + sizeof(mp_ipv6); + memcpy(¶m_cap_mp_ipv6.value, &cap_mp_ipv6, param_cap_mp_ipv6.len); + + data.opt_len = 2 + param_cap_mp_ipv6.len; + memcpy(&data.opt_params, ¶m_cap_mp_ipv6, data.opt_len); + } + else + data.opt_len = 0; + + memcpy(peer->outbuf->packet.data, &data, BGP_DATA_OPEN_SIZE + data.opt_len); + len += BGP_DATA_OPEN_SIZE + data.opt_len; peer->outbuf->packet.header.len = htons(len); peer->outbuf->done = 0; @@@ -1233,6 -1552,179 +1574,179 @@@ static int bgp_send_update(struct bgp_p return bgp_write(peer); } + /* send/buffer UPDATE message for IPv6 routes */ + static int bgp_send_update6(struct bgp_peer *peer) + { + uint16_t attr_len; + uint16_t unreach_len = 0; + char *unreach_len_pos; + uint8_t reach_len; + uint16_t len = sizeof(peer->outbuf->packet.header); + struct bgp_route6_list *have = peer->routes6; + struct bgp_route6_list *want = peer->routing ? bgp_routes6 : 0; + struct bgp_route6_list *e = 0; + struct bgp_route6_list *add = 0; + int s; + char ipv6addr[INET6_ADDRSTRLEN]; + + char *data = (char *) &peer->outbuf->packet.data; + + /* need leave room for attr_len, bgp_path_attrs and one prefix */ + char *max = (char *) &peer->outbuf->packet.data + + sizeof(peer->outbuf->packet.data) + - sizeof(attr_len) - peer->path_attr_len_without_nexthop + - BGP_PATH_ATTR_MP_REACH_NLRI_PARTIAL_SIZE - sizeof(struct bgp_ip6_prefix); + + memset(peer->outbuf->packet.header.marker, 0xff, + sizeof(peer->outbuf->packet.header.marker)); + + peer->outbuf->packet.header.type = BGP_MSG_UPDATE; + + /* insert non-MP unfeasible routes length */ + memcpy(data, &unreach_len, sizeof(unreach_len)); + /* skip over it and attr_len too; it will be filled when known */ + data += sizeof(unreach_len) + sizeof(attr_len); + len += sizeof(unreach_len) + sizeof(attr_len); + + /* copy usual attributes */ + memcpy(data, peer->path_attrs, peer->path_attr_len_without_nexthop); + data += peer->path_attr_len_without_nexthop; + attr_len = peer->path_attr_len_without_nexthop; + + /* copy MP unreachable NLRI heading */ + memcpy(data, peer->mp_unreach_nlri_partial, + BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE); + /* remember where to update this attr len */ + unreach_len_pos = data + 2; + data += BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE; + attr_len += BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE; + + peer->update_routes6 = 0; /* tentatively clear */ + + /* find differences */ + while ((have || want) && data < (max - sizeof(struct bgp_ip6_prefix))) + { + if (have) + s = want + ? memcmp(&have->dest, &want->dest, sizeof(have->dest)) + : -1; + else + s = 1; + + if (s < 0) /* found one to delete */ + { + struct bgp_route6_list *tmp = have; + have = have->next; + + s = BGP_IP_PREFIX_SIZE(tmp->dest); + memcpy(data, &tmp->dest, s); + data += s; + unreach_len += s; + attr_len += s; + + LOG(5, 0, 0, "Withdrawing route %s/%d from BGP peer %s\n", + inet_ntop(AF_INET6, &tmp->dest.prefix, ipv6addr, INET6_ADDRSTRLEN), + tmp->dest.len, peer->name); + + free(tmp); + + if (e) + e->next = have; + else + peer->routes6 = have; + } + else + { + if (!s) /* same */ + { + e = have; /* stash the last found to relink above */ + have = have->next; + want = want->next; + } + else if (s > 0) /* addition reqd. */ + { + if (add) + { + peer->update_routes6 = 1; /* only one add per packet */ + if (!have) + break; + } + else + add = want; + + if (want) + want = want->next; + } + } + } + + if (have || want) + peer->update_routes6 = 1; /* more to do */ + + /* anything changed? */ + if (!(unreach_len || add)) + return 1; + + if (unreach_len) + { + /* go back and insert MP unreach_len */ + unreach_len += sizeof(struct bgp_attr_mp_unreach_nlri_partial); + unreach_len = htons(unreach_len); + memcpy(unreach_len_pos, &unreach_len, sizeof(unreach_len)); + } + else + { + /* we can remove this attribute, then */ + data -= BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE; + attr_len -= BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE; + } + + if (add) + { + if (!(e = malloc(sizeof(*e)))) + { + LOG(0, 0, 0, "Can't allocate route for %s/%d (%s)\n", + inet_ntop(AF_INET6, &add->dest.prefix, ipv6addr, INET6_ADDRSTRLEN), + add->dest.len, strerror(errno)); + + return 0; + } + + memcpy(e, add, sizeof(*e)); + e->next = 0; + peer->routes6 = bgp_insert_route6(peer->routes6, e); + + /* copy MP reachable NLRI heading */ + memcpy(data, peer->mp_reach_nlri_partial, + BGP_PATH_ATTR_MP_REACH_NLRI_PARTIAL_SIZE); + /* with proper len */ + reach_len = BGP_IP_PREFIX_SIZE(add->dest); + data[2] = sizeof(struct bgp_attr_mp_reach_nlri_partial) + reach_len; + data += BGP_PATH_ATTR_MP_REACH_NLRI_PARTIAL_SIZE; + attr_len += BGP_PATH_ATTR_MP_REACH_NLRI_PARTIAL_SIZE; + + memcpy(data, &add->dest, reach_len); + data += reach_len; + attr_len += reach_len; + + LOG(5, 0, 0, "Advertising route %s/%d to BGP peer %s\n", + inet_ntop(AF_INET6, &add->dest.prefix, ipv6addr, INET6_ADDRSTRLEN), + add->dest.len, peer->name); + } + + /* update len with attributes we added */ + len += attr_len; + + /* go back and insert attr_len */ + attr_len = htons(attr_len); + memcpy((char *)&peer->outbuf->packet.data + 2, &attr_len, sizeof(attr_len)); + + peer->outbuf->packet.header.len = htons(len); + peer->outbuf->done = 0; + + return bgp_write(peer); + } + /* send/buffer NOTIFICATION message */ static int bgp_send_notification(struct bgp_peer *peer, uint8_t code, uint8_t subcode) diff --combined bgp.h index 1dbee29,fc2e517..bf429bd --- a/bgp.h +++ b/bgp.h @@@ -43,11 -43,51 +43,51 @@@ struct bgp_data_open char opt_params[sizeof(((struct bgp_packet *)0)->data) - BGP_DATA_OPEN_SIZE]; /* variable */ } __attribute__ ((packed)); + struct bgp_opt_param { + uint8_t type; + uint8_t len; + #define BGP_MAX_OPT_PARAM_SIZE 256 + char value[BGP_MAX_OPT_PARAM_SIZE]; + } __attribute__ ((packed)); + + #define BGP_PARAM_TYPE_CAPABILITY 2 + struct bgp_capability { + uint8_t code; + uint8_t len; + #define BGP_MAX_CAPABILITY_SIZE 256 + char value[BGP_MAX_CAPABILITY_SIZE]; + } __attribute__ ((packed)); + + /* RFC4760 Multiprotocol extension */ + #define BGP_CAP_CODE_MP 1 + + struct bgp_mp_cap_param { + uint16_t afi; /* sa_family_t */ + uint8_t reserved; /* SHOULD be 0 */ + uint8_t safi; + } __attribute__ ((packed)); + + /* bgp_mp_cap_param.afi */ + #define BGP_MP_AFI_RESERVED 0 + #define BGP_MP_AFI_IPv4 1 + #define BGP_MP_AFI_IPv6 2 + /* bgp_mp_cap_param.safi */ + #define BGP_MP_SAFI_UNICAST 1 + #define BGP_MP_SAFI_MULTICAST 2 + + struct bgp_ip6_prefix { + uint8_t len; + uint8_t prefix[16]; /* variable */ + } __attribute__ ((packed)); + + /* end of RFC4760 specific definitions */ + struct bgp_ip_prefix { uint8_t len; uint32_t prefix; /* variable */ } __attribute__ ((packed)); + /* works for both IPv4 and IPv6 prefixes */ #define BGP_IP_PREFIX_SIZE(p) (1 + ((p).len / 8) + ((p).len % 8 != 0)) struct bgp_path_attr { @@@ -65,6 -105,22 +105,22 @@@ } data; /* variable */ } __attribute__ ((packed)); + struct bgp_attr_mp_reach_nlri_partial { + uint16_t afi; /* sa_family_t */ + uint8_t safi; + uint8_t next_hop_len; + uint8_t next_hop[16]; + uint8_t reserved; + } __attribute__ ((packed)); + #define BGP_PATH_ATTR_MP_REACH_NLRI_PARTIAL_SIZE (3 + sizeof(struct bgp_attr_mp_reach_nlri_partial)) + + struct bgp_attr_mp_unreach_nlri_partial { + uint16_t afi; /* sa_family_t */ + uint8_t safi; + } __attribute__ ((packed)); + /* we use it as an extended attribute */ + #define BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE (4 + sizeof(struct bgp_attr_mp_unreach_nlri_partial)) + /* bgp_path_attr.flags (bitfields) */ #define BGP_PATH_ATTR_FLAG_OPTIONAL (1 << 7) #define BGP_PATH_ATTR_FLAG_TRANS (1 << 6) @@@ -85,9 -141,11 +141,11 @@@ #define BGP_PATH_ATTR_CODE_ATOMIC_AGGREGATE 6 /* well-known, discretionary */ #define BGP_PATH_ATTR_CODE_AGGREGATOR 7 /* optional, transitive */ #define BGP_PATH_ATTR_CODE_COMMUNITIES 8 /* optional, transitive (RFC1997) */ + #define BGP_PATH_ATTR_CODE_MP_REACH_NLRI 14 /* optional, non-transitive (RFC4760) */ + #define BGP_PATH_ATTR_CODE_MP_UNREACH_NLRI 15 /* optional, non-transitive (RFC4760) */ #define BGP_PATH_ATTR_SIZE(p) ((((p).flags & BGP_PATH_ATTR_FLAG_EXTLEN) \ - ? ((p).data.e.len + 1) : (p).data.s.len) + 3) + ? ((p).data.e.len + 4) : (p).data.s.len) + 3) /* well known COMMUNITIES */ #define BGP_COMMUNITY_NO_EXPORT 0xffffff01 /* don't advertise outside confederation */ @@@ -101,6 -159,7 +159,7 @@@ struct bgp_data_notification } __attribute__ ((packed)); /* bgp_data_notification.error_code, .error_subcode */ + #define BGP_ERR_UNSPEC 0 #define BGP_ERR_HEADER 1 # define BGP_ERR_HDR_NOT_SYNC 1 # define BGP_ERR_HDR_BAD_LEN 2 @@@ -112,6 -171,7 +171,7 @@@ # define BGP_ERR_OPN_UNSUP_PARAM 4 # define BGP_ERR_OPN_AUTH_FAILURE 5 # define BGP_ERR_OPN_HOLD_TIME 6 + # define BGP_ERR_OPN_UNSUP_CAP 7 #define BGP_ERR_UPDATE 3 # define BGP_ERR_UPD_BAD_ATTR_LIST 1 # define BGP_ERR_UPD_UNKN_WK_ATTR 2 @@@ -138,6 -198,11 +198,11 @@@ enum bgp_state Established, /* established */ }; + struct bgp_route6_list { + struct bgp_ip6_prefix dest; + struct bgp_route6_list *next; + }; + struct bgp_route_list { struct bgp_ip_prefix dest; struct bgp_route_list *next; @@@ -148,11 -213,16 +213,17 @@@ struct bgp_buf size_t done; /* bytes sent/recvd */ }; + enum bgp_mp_handling { + HandleIPv6Routes, + DoesntHandleIPv6Routes, + HandlingUnknown, + }; + /* state */ struct bgp_peer { char name[32]; /* peer name */ in_addr_t addr; /* peer address */ + in_addr_t source_addr; /* our source address */ int as; /* AS number */ int sock; enum bgp_state state; /* FSM state */ @@@ -174,8 -244,14 +245,14 @@@ int cli_flag; /* updates requested from CLI */ char *path_attrs; /* path attrs to send in UPDATE message */ int path_attr_len; /* length of path attrs */ + int path_attr_len_without_nexthop; /* length of path attrs without NEXT_HOP */ uint32_t events; /* events to poll */ struct event_data edata; /* poll data */ + enum bgp_mp_handling mp_handling; /* how it handles IPv6 routes advertisements */ + int update_routes6; /* UPDATE required for IPv6 routes */ + struct bgp_route6_list *routes6; /* IPv6 routes known by this peer */ + char mp_reach_nlri_partial[BGP_PATH_ATTR_MP_REACH_NLRI_PARTIAL_SIZE]; + char mp_unreach_nlri_partial[BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE]; }; /* bgp_peer.cli_flag */ @@@ -189,13 -265,15 +266,15 @@@ extern int bgp_configured /* actions */ int bgp_setup(int as); int bgp_start(struct bgp_peer *peer, char *name, int as, int keepalive, - int hold, int enable); + int hold, struct in_addr update_source, int enable); void bgp_stop(struct bgp_peer *peer); void bgp_halt(struct bgp_peer *peer); int bgp_restart(struct bgp_peer *peer); int bgp_add_route(in_addr_t ip, in_addr_t mask); + int bgp_add_route6(struct in6_addr ip, int prefixlen); int bgp_del_route(in_addr_t ip, in_addr_t mask); + int bgp_del_route6(struct in6_addr ip, int prefixlen); void bgp_enable_routing(int enable); int bgp_set_poll(void); int bgp_process(uint32_t events[]); diff --combined etc/startup-config.default index 1b88dc2,4a3ac84..f86e2aa --- a/etc/startup-config.default +++ b/etc/startup-config.default @@@ -7,9 -7,6 +7,9 @@@ set log_file "/var/log/l2tpns # Write pid to this file set pid_file "/var/run/l2tpns.pid" +# This host name, if different from the OS one +#set hostname "localhost" + # Shared secret with LAC set l2tp_secret "secret" @@@ -56,9 -53,6 +56,9 @@@ set accounting_dir "/var/run/l2tpns/acc # Listen address for L2TP #set bind_address 1.1.1.1 +# Listen address for CLI +#set cli_bind_address 127.0.0.1 + # Send a gratiuitous ARP for bind address #set send_garp no @@@ -108,6 -102,10 +108,10 @@@ # IPv6 address prefix #set ipv6_prefix :: + # BGP NEXT_HOP path attribute + #set nexthop 10.0.1.1 + #set nexthop6 2001:db8::1 + # Drop/kill sessions #load plugin "sessionctl" diff --combined l2tpns.c index 39b09b5,6a4656e..61b2184 --- a/l2tpns.c +++ b/l2tpns.c @@@ -69,7 -69,7 +69,7 @@@ int rand_fd = -1; // Random data sourc int cluster_sockfd = -1; // Intra-cluster communications socket. int epollfd = -1; // event polling time_t basetime = 0; // base clock -char hostname[1000] = ""; // us. +char hostname[MAXHOSTNAME] = ""; // us. static int tunidx; // ifr_ifindex of tun device static int syslog_log = 0; // are we logging to syslog static FILE *log_stream = 0; // file handle for direct logging (i.e. direct into file, not via syslog). @@@ -157,9 -157,7 +157,10 @@@ config_descriptt config_values[] = CONFIG("cluster_hb_timeout", cluster_hb_timeout, INT), CONFIG("cluster_master_min_adv", cluster_master_min_adv, INT), CONFIG("ipv6_prefix", ipv6_prefix, IPv6), + CONFIG("cli_bind_address", cli_bind_address, IPv4), + CONFIG("hostname", hostname, STRING), + CONFIG("nexthop_address", nexthop_address, IPv4), + CONFIG("nexthop6_address", nexthop6_address, IPv6), { NULL, 0, 0, 0 }, }; @@@ -167,6 -165,8 +168,6 @@@ static char *plugin_functions[] = NULL, "plugin_pre_auth", "plugin_post_auth", - "plugin_packet_rx", - "plugin_packet_tx", "plugin_timer", "plugin_new_session", "plugin_kill_session", @@@ -502,7 -502,12 +503,12 @@@ void route6set(sessionidt s, struct in6 LOG(0, 0, 0, "route6set() error in ioctl: %s\n", strerror(errno)); - // FIXME: need to add BGP routing (RFC2858) + #ifdef BGP + if (add) + bgp_add_route6(ip, prefixlen); + else + bgp_del_route6(ip, prefixlen); + #endif /* BGP */ if (s) { @@@ -3398,36 -3403,35 +3404,36 @@@ static int still_busy(void static clockt last_talked = 0; static clockt start_busy_wait = 0; - if (!config->cluster_iam_master) - { #ifdef BGP - static time_t stopped_bgp = 0; - if (bgp_configured) + static time_t stopped_bgp = 0; + if (bgp_configured) + { + if (!stopped_bgp) { - if (!stopped_bgp) - { - LOG(1, 0, 0, "Shutting down in %d seconds, stopping BGP...\n", QUIT_DELAY); + LOG(1, 0, 0, "Shutting down in %d seconds, stopping BGP...\n", QUIT_DELAY); - for (i = 0; i < BGP_NUM_PEERS; i++) - if (bgp_peers[i].state == Established) - bgp_stop(&bgp_peers[i]); + for (i = 0; i < BGP_NUM_PEERS; i++) + if (bgp_peers[i].state == Established) + bgp_stop(&bgp_peers[i]); - stopped_bgp = time_now; + stopped_bgp = time_now; + if (!config->cluster_iam_master) + { // we don't want to become master cluster_send_ping(0); return 1; } - - if (time_now < (stopped_bgp + QUIT_DELAY)) - return 1; } + + if (!config->cluster_iam_master && time_now < (stopped_bgp + QUIT_DELAY)) + return 1; + } #endif /* BGP */ + if (!config->cluster_iam_master) return 0; - } if (main_quit == QUIT_SHUTDOWN) { @@@ -3568,8 -3572,7 +3574,8 @@@ static void mainloop(void if (config->neighbour[i].name[0]) bgp_start(&bgp_peers[i], config->neighbour[i].name, config->neighbour[i].as, config->neighbour[i].keepalive, - config->neighbour[i].hold, 0); /* 0 = routing disabled */ + config->neighbour[i].hold, config->neighbour[i].update_source, + 0); /* 0 = routing disabled */ } #endif /* BGP */ @@@ -4070,14 -4073,9 +4076,14 @@@ static void initdata(int optdebug, cha if (!*hostname) { - // Grab my hostname unless it's been specified - gethostname(hostname, sizeof(hostname)); - stripdomain(hostname); + if (!*config->hostname) + { + // Grab my hostname unless it's been specified + gethostname(hostname, sizeof(hostname)); + stripdomain(hostname); + } + else + strcpy(hostname, config->hostname); } _statistics->start_time = _statistics->last_reset = time(NULL); diff --combined l2tpns.h index e6357b6,55de73f..4f7fad9 --- a/l2tpns.h +++ b/l2tpns.h @@@ -38,7 -38,6 +38,7 @@@ #define PPPoE_MRU 1492 // maximum PPPoE MRU (rfc2516: 1500 less PPPoE header (6) and PPP protocol ID (2)) #define MAXETHER (MAXMTU+18) // max packet we try sending to tun #define MAXTEL 96 // telephone number +#define MAXHOSTNAME 256 // hostname #define MAXUSER 128 // username #define MAXPASS 128 // password #define MAXPLUGINS 20 // maximum number of plugins to load @@@ -713,8 -712,6 +713,8 @@@ typedef struc int cluster_master_min_adv; // Master advertises routes while the number of up to date // slaves is less than this value. + in_addr_t cli_bind_address; // bind address for CLI + char hostname[MAXHOSTNAME]; // hostname (overridden by -h on command line) // Guest change char guest_user[MAXUSER]; // Guest account username @@@ -726,9 -723,8 +726,10 @@@ uint16_t as; int keepalive; int hold; + struct in_addr update_source; } neighbour[BGP_NUM_PEERS]; + in_addr_t nexthop_address; + struct in6_addr nexthop6_address; #endif } configt;