From: Benjamin Cama Date: Sat, 20 Aug 2011 16:25:44 +0000 (+0200) Subject: Merge branch 'use-netlink' into fdn-mods X-Git-Tag: debian/2.2.1-1fdn2~1^2~2 X-Git-Url: http://git.sameswireless.fr/l2tpns.git/commitdiff_plain/0f85bdecaba4806c0f06ee89d7ec084cc015bb19?hp=c23b9c14abdbb0fa3c5d94fa137ac38e3b13a71a Merge branch 'use-netlink' into fdn-mods --- diff --git a/bgp.c b/bgp.c index f1d0ec2..4b8cb64 100644 --- a/bgp.c +++ b/bgp.c @@ -32,8 +32,12 @@ static void bgp_set_retry(struct bgp_peer *peer); static void bgp_cidr(in_addr_t ip, int prefixlen, 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_process_timers(struct bgp_peer *peer); 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 +47,13 @@ static int bgp_handle_input(struct bgp_peer *peer); 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 +94,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 +102,7 @@ int bgp_setup(int as) /* 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,6 +131,7 @@ int bgp_start(struct bgp_peer *peer, char *name, int as, int keepalive, } 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; @@ -191,15 +199,6 @@ int bgp_start(struct bgp_peer *peer, char *name, int as, int keepalive, ADD_ATTRIBUTE(); - /* NEXT_HOP */ - a.flags = BGP_PATH_ATTR_FLAG_TRANS; - a.code = BGP_PATH_ATTR_CODE_NEXT_HOP; - 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; @@ -229,6 +228,25 @@ int bgp_start(struct bgp_peer *peer, char *name, int as, int keepalive, 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; + 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", @@ -239,6 +257,53 @@ int bgp_start(struct bgp_peer *peer, char *name, int as, int keepalive, 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"); @@ -267,6 +332,8 @@ static void bgp_clear(struct bgp_peer *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; @@ -354,6 +421,33 @@ static struct bgp_route_list *bgp_insert_route(struct bgp_route_list *head, 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 @@ -407,6 +501,59 @@ int bgp_add_route(in_addr_t ip, int prefixlen) 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, int prefixlen) { @@ -457,6 +604,58 @@ int bgp_del_route(in_addr_t ip, int prefixlen) 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) { @@ -601,36 +800,68 @@ int bgp_process(uint32_t events[]) continue; } - /* process timers */ - if (peer->state == Established) + /* process pending IPv6 updates */ + if (peer->update_routes6 + && !peer->outbuf->packet.header.len) /* ditto */ { - if (time_now > peer->expire_time) - { - LOG(1, 0, 0, "No message from BGP peer %s in %ds\n", - peer->name, peer->hold); - - bgp_send_notification(peer, BGP_ERR_HOLD_TIMER_EXP, 0); + if (!bgp_send_update6(peer)) continue; - } - - if (time_now > peer->keepalive_time && !peer->outbuf->packet.header.len) - bgp_send_keepalive(peer); - } - else if (peer->state == Idle) - { - if (time_now > peer->retry_time) - bgp_connect(peer); } - else if (time_now > peer->state_time + BGP_STATE_TIME) + + /* process timers */ + bgp_process_timers(peer); + } + + return 1; +} + +/* process bgp timers only */ +void bgp_process_peers_timers() +{ + int i; + + if (!bgp_configured) + return; + + for (i = 0; i < BGP_NUM_PEERS; i++) + { + struct bgp_peer *peer = &bgp_peers[i]; + + if (peer->state == Disabled) + continue; + + bgp_process_timers(peer); + } +} + +static void bgp_process_timers(struct bgp_peer *peer) +{ + if (peer->state == Established) + { + if (time_now > peer->expire_time) { - LOG(1, 0, 0, "%s timer expired for BGP peer %s\n", - bgp_state_str(peer->state), peer->name); + LOG(1, 0, 0, "No message from BGP peer %s in %ds\n", + peer->name, peer->hold); - bgp_restart(peer); + bgp_send_notification(peer, BGP_ERR_HOLD_TIMER_EXP, 0); + return; } + + if (time_now > peer->keepalive_time && !peer->outbuf->packet.header.len) + bgp_send_keepalive(peer); } + else if (peer->state == Idle) + { + if (time_now > peer->retry_time) + bgp_connect(peer); + } + else if (time_now > peer->state_time + BGP_STATE_TIME) + { + LOG(1, 0, 0, "%s timer expired for BGP peer %s\n", + bgp_state_str(peer->state), peer->name); - return 1; + bgp_restart(peer); + } } static void bgp_free_routes(struct bgp_route_list *routes) @@ -644,6 +875,17 @@ static void bgp_free_routes(struct bgp_route_list *routes) } } +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) @@ -678,6 +920,7 @@ static int bgp_connect(struct bgp_peer *peer) { static int bgp_port = 0; struct sockaddr_in addr; + struct sockaddr_in source_addr; struct epoll_event ev; if (!bgp_port) @@ -709,6 +952,19 @@ static int bgp_connect(struct bgp_peer *peer) /* 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; @@ -884,6 +1140,12 @@ static int bgp_handle_input(struct bgp_peer *peer) 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++) { @@ -946,10 +1208,93 @@ static int bgp_handle_input(struct bgp_peer *peer) 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; @@ -978,10 +1323,30 @@ static int bgp_handle_input(struct bgp_peer *peer) 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); @@ -1012,6 +1377,9 @@ static int bgp_handle_input(struct bgp_peer *peer) 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, @@ -1022,11 +1390,35 @@ static int bgp_send_open(struct bgp_peer *peer) data.version = BGP_VERSION; data.as = htons(our_as); data.hold_time = htons(peer->hold); - data.identifier = my_address; - data.opt_len = 0; + /* use the source IP we use as identifier, if available */ + if (peer->source_addr != INADDR_ANY) + data.identifier = peer->source_addr; + else + data.identifier = my_address; + + /* 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); - len += BGP_DATA_OPEN_SIZE; + 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; @@ -1193,6 +1585,179 @@ static int bgp_send_update(struct bgp_peer *peer) 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 --git a/bgp.h b/bgp.h index f8b52d6..bc5a0e9 100644 --- a/bgp.h +++ b/bgp.h @@ -43,11 +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 @@ struct bgp_path_attr { } 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 @@ struct bgp_path_attr { #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 @@ 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 @@ struct bgp_data_notification { # 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 @@ 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,10 +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 */ @@ -173,8 +245,14 @@ struct bgp_peer { 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 */ @@ -188,16 +266,19 @@ 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, int prefixlen); +int bgp_add_route6(struct in6_addr ip, int prefixlen); int bgp_del_route(in_addr_t ip, int prefixlen); +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[]); +void bgp_process_peers_timers(); char const *bgp_state_str(enum bgp_state state); extern char const *cvs_id_bgp; diff --git a/cli.c b/cli.c index 7fbe3d1..9433106 100644 --- a/cli.c +++ b/cli.c @@ -283,6 +283,7 @@ void init_cli(char *hostname) fcntl(clifd, F_SETFL, flags | O_NONBLOCK); } addr.sin_family = AF_INET; + addr.sin_addr.s_addr = config->cli_bind_address; /* defaults to INADDR_ANY */ addr.sin_port = htons(23); if (bind(clifd, (void *) &addr, sizeof(addr)) < 0) { @@ -443,6 +444,14 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int if (session[s].idle_timeout) cli_print(cli, "\tIdle Timeout:\t%u seconds", session[s].idle_timeout - (session[s].last_data ? abs(time_now - session[s].last_data) : 0)); + if (session[s].timeout) + { + cli_print(cli, "\tRemaining time:\t%u", + (session[s].bundle && bundle[session[s].bundle].num_of_links > 1) + ? (unsigned) (session[s].timeout - bundle[session[s].bundle].online_time) + : (unsigned) (session[s].timeout - (time_now - session[s].opened))); + } + cli_print(cli, "\tBytes In/Out:\t%u/%u", session[s].cout, session[s].cin); cli_print(cli, "\tPkts In/Out:\t%u/%u", session[s].pout, session[s].pin); cli_print(cli, "\tMRU:\t\t%d", session[s].mru); @@ -510,7 +519,7 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int } // Show Summary - cli_print(cli, "%5s %4s %-32s %-15s %s %s %s %s %10s %10s %10s %4s %-15s %s", + cli_print(cli, "%5s %4s %-32s %-15s %s %s %s %s %10s %10s %10s %4s %10s %-15s %s", "SID", "TID", "Username", @@ -523,13 +532,20 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int "downloaded", "uploaded", "idle", + "Rem.Time", "LAC", "CLI"); for (i = 1; i < MAXSESSION; i++) { + uint32_t rem_time; if (!session[i].opened) continue; - cli_print(cli, "%5d %4d %-32s %-15s %s %s %s %s %10u %10lu %10lu %4u %-15s %s", + if (session[i].bundle && bundle[session[i].bundle].num_of_links > 1) + rem_time = session[i].timeout ? (session[i].timeout - bundle[session[i].bundle].online_time) : 0; + else + rem_time = session[i].timeout ? (session[i].timeout - (time_now-session[i].opened)) : 0; + + cli_print(cli, "%5d %4d %-32s %-15s %s %s %s %s %10u %10lu %10lu %4u %10lu %-15s %s", i, session[i].tunnel, session[i].user[0] ? session[i].user : "*", @@ -542,6 +558,7 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int (unsigned long)session[i].cout, (unsigned long)session[i].cin, abs(time_now - (session[i].last_packet ? session[i].last_packet : time_now)), + (unsigned long)(rem_time), fmtaddr(htonl(tunnel[ session[i].tunnel ].ip), 1), session[i].calling[0] ? session[i].calling : "*"); } @@ -1022,6 +1039,11 @@ static int cmd_show_run(struct cli_def *cli, char *command, char **argv, int arg h = BGP_HOLD_TIME; cli_print(cli, " neighbour %s timers %d %d", config->neighbour[i].name, k, h); + + if (config->neighbour[i].update_source.s_addr != INADDR_ANY) + cli_print(cli, " neighbour %s update-source %s", + config->neighbour[i].name, + inet_ntoa(config->neighbour[i].update_source)); } } #endif @@ -2048,6 +2070,7 @@ static int cmd_router_bgp_neighbour(struct cli_def *cli, char *command, char **a return cli_arg_help(cli, 0, "remote-as", "Set remote autonomous system number", "timers", "Set timers", + "update-source", "Set source address to use for the BGP session", NULL); default: @@ -2066,6 +2089,9 @@ static int cmd_router_bgp_neighbour(struct cli_def *cli, char *command, char **a return cli_arg_help(cli, 1, NULL); } + if (MATCH("update-source", argv[1])) + return cli_arg_help(cli, argc > 3, "A.B.C.D", "Source IP address", NULL); + return CLI_OK; } } @@ -2102,12 +2128,33 @@ static int cmd_router_bgp_neighbour(struct cli_def *cli, char *command, char **a snprintf(config->neighbour[i].name, sizeof(config->neighbour[i].name), "%s", argv[0]); config->neighbour[i].keepalive = -1; config->neighbour[i].hold = -1; + config->neighbour[i].update_source.s_addr = INADDR_ANY; } config->neighbour[i].as = as; return CLI_OK; } + if (MATCH("update-source", argv[1])) + { + struct in_addr addr; + + if (!config->neighbour[i].name[0]) + { + cli_error(cli, "Specify remote-as first"); + return CLI_OK; + } + + if (!inet_aton(argv[2], &addr)) + { + cli_error(cli, "Cannot parse IP \"%s\"", argv[2]); + return CLI_OK; + } + + config->neighbour[i].update_source = addr; + return CLI_OK; + } + if (argc != 4 || !MATCH("timers", argv[1])) { cli_error(cli, "Invalid arguments"); diff --git a/etc/startup-config.default b/etc/startup-config.default index 77425c8..f86e2aa 100644 --- a/etc/startup-config.default +++ b/etc/startup-config.default @@ -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" @@ -53,6 +56,9 @@ set accounting_dir "/var/run/l2tpns/acct" # 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 @@ -102,6 +108,10 @@ set accounting_dir "/var/run/l2tpns/acct" # 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 --git a/l2tpns.c b/l2tpns.c index bab453f..ae7f3f4 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -69,7 +69,7 @@ int rand_fd = -1; // Random data source 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 int nlseqnum = 0; // netlink sequence number int min_initok_nlseqnum = 0; // minimun seq number for messages after init is ok @@ -159,6 +159,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 }, }; @@ -166,8 +170,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", @@ -550,7 +552,12 @@ void route6set(sessionidt s, struct in6_addr ip, int prefixlen, int add) if (netlink_send(&req.nh) < 0) LOG(0, 0, 0, "route6set() error in sending netlink message: %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) { @@ -3580,35 +3587,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) { @@ -3753,7 +3761,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 */ @@ -3973,6 +3982,11 @@ static void mainloop(void) more++; } } +#ifdef BGP + else + /* no event received, but timers could still have expired */ + bgp_process_peers_timers(); +#endif /* BGP */ if (time_changed) { @@ -4280,9 +4294,14 @@ static void initdata(int optdebug, char *optconfig) 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 --git a/l2tpns.h b/l2tpns.h index bab2921..5cba32c 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -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 @@ -716,6 +717,8 @@ typedef struct 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 @@ -727,7 +730,10 @@ typedef struct 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; diff --git a/ppp.c b/ppp.c index 7942e6e..7be79ce 100644 --- a/ppp.c +++ b/ppp.c @@ -821,6 +821,10 @@ void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l) break; + case Closing: + sessionshutdown(s, "LCP: ConfigReq in state Closing. This should not happen. Killing session.", CDN_ADMIN_DISC, TERM_LOST_SERVICE); + break; + default: LOG(2, s, t, "LCP: ignoring %s in state %s\n", ppp_code(*p), ppp_state(session[s].ppp.lcp)); return;