diff options
author | Michele Marchetto <michele@cvs.openbsd.org> | 2009-06-01 20:59:46 +0000 |
---|---|---|
committer | Michele Marchetto <michele@cvs.openbsd.org> | 2009-06-01 20:59:46 +0000 |
commit | 5de1d23aeb3294b99aea577259871a4a7686e9b4 (patch) | |
tree | cc434b947408b0844b7b33a08358359751c9a7e4 /usr.sbin | |
parent | 19268a0235511684a7ee9e71b0c3dd8170ff3762 (diff) |
Welcome ldpd, the Label Distribution Protocol daemon.
Built using the imsg/three process framework, its main aim
is to redistribute MPLS labels between peers.
Right now it has some really basic functionalities,
the basic protocol works and peers are able to exchange
labels and insert them in the kernel.
It still does not react to changes of topology.
Not yet connected to the builds.
ok claudio@ deraadt@
Diffstat (limited to 'usr.sbin')
35 files changed, 11935 insertions, 0 deletions
diff --git a/usr.sbin/ldpctl/Makefile b/usr.sbin/ldpctl/Makefile new file mode 100644 index 00000000000..2fe5a389f38 --- /dev/null +++ b/usr.sbin/ldpctl/Makefile @@ -0,0 +1,15 @@ +# $OpenBSD: Makefile,v 1.1 2009/06/01 20:59:45 michele Exp $ + +.PATH: ${.CURDIR}/../ldpd + +PROG= ldpctl +SRCS= buffer.c imsg.c log.c ldpctl.c parser.c +CFLAGS+= -Wall +CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes +CFLAGS+= -Wmissing-declarations +CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual +CFLAGS+= -Wsign-compare +CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../ldpd +MAN= ldpctl.8 + +.include <bsd.prog.mk> diff --git a/usr.sbin/ldpctl/ldpctl.8 b/usr.sbin/ldpctl/ldpctl.8 new file mode 100644 index 00000000000..56409aa53ba --- /dev/null +++ b/usr.sbin/ldpctl/ldpctl.8 @@ -0,0 +1,83 @@ +.\" $OpenBSD: ldpctl.8,v 1.1 2009/06/01 20:59:45 michele Exp $ +.\" +.\" Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> +.\" Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: June 1 2009 $ +.Dt LDPCTL 8 +.Os +.Sh NAME +.Nm ldpctl +.Nd control the Label Distribution Protocol daemon +.Sh SYNOPSIS +.Nm +.Ar command +.Op Ar argument ... +.Sh DESCRIPTION +The +.Nm +program controls the +.Xr ldpd 8 +daemon. +.Pp +The following commands are available: +.Bl -tag -width Ds +.It Cm lfib couple +Insert the learned routes into the Label Forwarding Information Base a.k.a. +the kernel routing table. +.It Cm lfib decouple +Remove the learned routes from the Label Forwarding Information Base a.k.a. +the kernel routing table. +.It Cm show lfib Op Ar destination | filter +Show the Label Forwarding Information Base. +.Ar destination +can be specified to show the route matching a destination IP address. +.Ar filter +can be any of the following: +.Pp +.Bl -tag -width "interfaceXXinterfaceXX" -compact +.It Cm connected +Show only connected routes. +.It Cm interface +Show only interfaces. +.It Cm static +Show only static routes. +.El +.Pp +.Cm connected , +and +.Cm static +may be specified together. +.It Cm show interfaces +Show details for all interfaces. +.It Cm show neighbor +Show neighbors. +.It Cm show lib +Show the Label Information Base. +.El +.Sh FILES +.Bl -tag -width "/var/run/ldpd.sockXX" -compact +.It /var/run/ldpd.sock +Unix-domain socket used for communication with +.Xr ldpd 8 . +.El +.Sh SEE ALSO +.Xr ldpd.conf 5 , +.Xr ldpd 8 +.Sh HISTORY +The +.Nm +program first appeared in +.Ox 4.6 . diff --git a/usr.sbin/ldpctl/ldpctl.c b/usr.sbin/ldpctl/ldpctl.c new file mode 100644 index 00000000000..41613f0ceb5 --- /dev/null +++ b/usr.sbin/ldpctl/ldpctl.c @@ -0,0 +1,551 @@ +/* $OpenBSD: ldpctl.c,v 1.1 2009/06/01 20:59:45 michele Exp $ + * + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> + * Copyright (c) 2003 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <net/if_media.h> +#include <net/if_types.h> +#include <netmpls/mpls.h> + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "ldp.h" +#include "ldpd.h" +#include "ldpe.h" +#include "parser.h" + +__dead void usage(void); +const char *fmt_timeframe_core(time_t); +const char *get_linkstate(int, int); +int show_interface_msg(struct imsg *); +int get_ifms_type(int); +int show_lib_msg(struct imsg *); +int show_nbr_msg(struct imsg *); +void show_lfib_head(void); +int show_lfib_msg(struct imsg *); +void show_interface_head(void); +int show_lfib_interface_msg(struct imsg *); +const char *get_media_descr(int); +void print_baudrate(u_int64_t); + +struct imsgbuf *ibuf; + +__dead void +usage(void) +{ + extern char *__progname; + + fprintf(stderr, "usage: %s command [argument ...]\n", __progname); + exit(1); +} + +/* dummy function to allow ldpctl to run without libevent */ +void +imsg_event_add(struct imsgbuf *i) +{ +} + +int +main(int argc, char *argv[]) +{ + struct sockaddr_un sun; + struct parse_result *res; + struct imsg imsg; + unsigned int ifidx = 0; + int ctl_sock; + int done = 0; + int n; + + /* parse options */ + if ((res = parse(argc - 1, argv + 1)) == NULL) + exit(1); + + /* connect to ldpd control socket */ + if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) + err(1, "socket"); + + bzero(&sun, sizeof(sun)); + sun.sun_family = AF_UNIX; + strlcpy(sun.sun_path, LDPD_SOCKET, sizeof(sun.sun_path)); + if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) + err(1, "connect: %s", LDPD_SOCKET); + + if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL) + err(1, NULL); + imsg_init(ibuf, ctl_sock, NULL); + done = 0; + + /* process user request */ + switch (res->action) { + case NONE: + usage(); + /* not reached */ + case SHOW: + case SHOW_IFACE: + printf("%-11s %-18s %-10s %-10s %-8s\n", + "Interface", "Address", "State", "Linkstate", + "Uptime"); + if (*res->ifname) { + ifidx = if_nametoindex(res->ifname); + if (ifidx == 0) + errx(1, "no such interface %s", res->ifname); + } + imsg_compose(ibuf, IMSG_CTL_SHOW_INTERFACE, 0, 0, + &ifidx, sizeof(ifidx)); + break; + case SHOW_NBR: + printf("%-15s %-15s %-15s %-9s %-10s\n", "ID", + "State", "Address", "Iface", "Uptime"); + imsg_compose(ibuf, IMSG_CTL_SHOW_NBR, 0, 0, NULL, 0); + break; + case SHOW_LIB: + printf("%-20s %-17s %-17s %s\n", "Destination", + "Nexthop", "Local Label", "Remote Label"); + imsg_compose(ibuf, IMSG_CTL_SHOW_LIB, 0, 0, NULL, 0); + break; + case SHOW_LFIB: + if (!res->addr.s_addr) + imsg_compose(ibuf, IMSG_CTL_KROUTE, 0, 0, + &res->flags, sizeof(res->flags)); + else + imsg_compose(ibuf, IMSG_CTL_KROUTE_ADDR, 0, 0, + &res->addr, sizeof(res->addr)); + show_lfib_head(); + break; + case SHOW_LFIB_IFACE: + if (*res->ifname) + imsg_compose(ibuf, IMSG_CTL_IFINFO, 0, 0, + res->ifname, sizeof(res->ifname)); + else + imsg_compose(ibuf, IMSG_CTL_IFINFO, 0, 0, NULL, 0); + show_interface_head(); + break; + case LFIB: + errx(1, "lfib couple|decouple"); + break; + case LFIB_COUPLE: + imsg_compose(ibuf, IMSG_CTL_LFIB_COUPLE, 0, 0, NULL, 0); + printf("couple request sent.\n"); + done = 1; + break; + case LFIB_DECOUPLE: + imsg_compose(ibuf, IMSG_CTL_LFIB_DECOUPLE, 0, 0, NULL, 0); + printf("decouple request sent.\n"); + done = 1; + break; + case RELOAD: + imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, NULL, 0); + printf("reload request sent.\n"); + done = 1; + break; + } + + while (ibuf->w.queued) + if (msgbuf_write(&ibuf->w) < 0) + err(1, "write error"); + + while (!done) { + if ((n = imsg_read(ibuf)) == -1) + errx(1, "imsg_read error"); + if (n == 0) + errx(1, "pipe closed"); + + while (!done) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + errx(1, "imsg_get error"); + if (n == 0) + break; + switch (res->action) { + case SHOW: + case SHOW_IFACE: + done = show_interface_msg(&imsg); + break; + case SHOW_NBR: + done = show_nbr_msg(&imsg); + break; + case SHOW_LIB: + done = show_lib_msg(&imsg); + break; + case SHOW_LFIB: + done = show_lfib_msg(&imsg); + break; + case SHOW_LFIB_IFACE: + done = show_lfib_interface_msg(&imsg); + break; + case NONE: + case LFIB: + case LFIB_COUPLE: + case LFIB_DECOUPLE: + case RELOAD: + break; + } + imsg_free(&imsg); + } + } + close(ctl_sock); + free(ibuf); + + return (0); +} + +int +get_ifms_type(int mediatype) +{ + switch (mediatype) { + case IFT_ETHER: + return (IFM_ETHER); + break; + case IFT_FDDI: + return (IFM_FDDI); + break; + case IFT_CARP: + return (IFM_CARP); + break; + default: + return (0); + break; + } +} + +#define TF_BUFS 8 +#define TF_LEN 9 + +const char * +fmt_timeframe_core(time_t t) +{ + char *buf; + static char tfbuf[TF_BUFS][TF_LEN]; /* ring buffer */ + static int idx = 0; + unsigned int sec, min, hrs, day, week; + + if (t == 0) + return ("Stopped"); + + buf = tfbuf[idx++]; + if (idx == TF_BUFS) + idx = 0; + + week = t; + + sec = week % 60; + week /= 60; + min = week % 60; + week /= 60; + hrs = week % 24; + week /= 24; + day = week % 7; + week /= 7; + + if (week > 0) + snprintf(buf, TF_LEN, "%02uw%01ud%02uh", week, day, hrs); + else if (day > 0) + snprintf(buf, TF_LEN, "%01ud%02uh%02um", day, hrs, min); + else + snprintf(buf, TF_LEN, "%02u:%02u:%02u", hrs, min, sec); + + return (buf); +} + +/* prototype defined in ldpd.h and shared with the kroute.c version */ +u_int8_t +mask2prefixlen(in_addr_t ina) +{ + if (ina == 0) + return (0); + else + return (33 - ffs(ntohl(ina))); +} + +int +show_interface_msg(struct imsg *imsg) +{ + struct ctl_iface *iface; + char *netid; + + switch (imsg->hdr.type) { + case IMSG_CTL_SHOW_INTERFACE: + iface = imsg->data; + + if (asprintf(&netid, "%s/%d", inet_ntoa(iface->addr), + mask2prefixlen(iface->mask.s_addr)) == -1) + err(1, NULL); + printf("%-11s %-18s %-10s %-10s %-8s\n", + iface->name, netid, if_state_name(iface->state), + get_linkstate(get_ifms_type(iface->mediatype), + iface->linkstate), iface->uptime == 0 ? "00:00:00" : + fmt_timeframe_core(iface->uptime)); + free(netid); + break; + case IMSG_CTL_END: + printf("\n"); + return (1); + default: + break; + } + + return (0); +} + +int +show_lib_msg(struct imsg *imsg) +{ + struct ctl_rt *rt; + char *dstnet; + + switch (imsg->hdr.type) { + case IMSG_CTL_SHOW_LIB: + rt = imsg->data; + if (asprintf(&dstnet, "%s/%d", inet_ntoa(rt->prefix), + rt->prefixlen) == -1) + err(1, NULL); + + printf("%-20s %-17s %-17u %u\n", dstnet, + inet_ntoa(rt->nexthop), + (ntohl(rt->local_label) >> MPLS_LABEL_OFFSET), + (ntohl(rt->remote_label) >> MPLS_LABEL_OFFSET)); + free(dstnet); + + break; + case IMSG_CTL_END: + printf("\n"); + return (1); + default: + break; + } + + return (0); +} + +int +show_nbr_msg(struct imsg *imsg) +{ + struct ctl_nbr *nbr; + char *state; + + switch (imsg->hdr.type) { + case IMSG_CTL_SHOW_NBR: + nbr = imsg->data; + if (asprintf(&state, "%s/%s", nbr_state_name(nbr->nbr_state), + if_state_name(nbr->iface_state)) == -1) + err(1, NULL); + printf("%-15s %-16s", inet_ntoa(nbr->id), + state); + printf("%-15s %-10s", inet_ntoa(nbr->addr), nbr->name); + printf("%-15s\n", nbr->uptime == 0 ? "-" : + fmt_timeframe_core(nbr->uptime)); + free(state); + break; + case IMSG_CTL_END: + printf("\n"); + return (1); + default: + break; + } + + return (0); +} + +void +show_lfib_head(void) +{ + printf("flags: * = valid, C = Connected, S = Static\n"); + printf("%-6s %-20s %-17s %-17s %s\n", "Flags", "Destination", "Nexthop", + "Local Label", "Remote Label"); +} + +int +show_lfib_msg(struct imsg *imsg) +{ + struct kroute *k; + char *p; + + switch (imsg->hdr.type) { + case IMSG_CTL_KROUTE: + if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(struct kroute)) + errx(1, "wrong imsg len"); + k = imsg->data; + + if (k->flags & F_DOWN) + printf(" "); + else + printf("*"); + + if (!(k->flags & F_KERNEL)) + printf("R"); + else if (k->flags & F_CONNECTED) + printf("C"); + else if (k->flags & F_STATIC) + printf("S"); + else + printf(" "); + + printf(" "); + if (asprintf(&p, "%s/%u", inet_ntoa(k->prefix), + k->prefixlen) == -1) + err(1, NULL); + printf("%-20s ", p); + free(p); + + if (k->nexthop.s_addr) + printf("%-18s", inet_ntoa(k->nexthop)); + else if (k->flags & F_CONNECTED) + printf("link#%-13u", k->ifindex); + + if (k->local_label) { + printf("%-18u", (ntohl(k->local_label) >> + MPLS_LABEL_OFFSET)); + } else + printf("- "); + + if (k->remote_label) { + printf("%u", (ntohl(k->remote_label) >> + MPLS_LABEL_OFFSET)); + } else + printf("-"); + + printf("\n"); + + break; + case IMSG_CTL_END: + printf("\n"); + return (1); + default: + break; + } + + return (0); +} + +void +show_interface_head(void) +{ + printf("%-15s%-15s%s\n", "Interface", "Flags", + "Link state"); +} + +int +show_lfib_interface_msg(struct imsg *imsg) +{ + struct kif *k; + int ifms_type; + + switch (imsg->hdr.type) { + case IMSG_CTL_IFINFO: + k = imsg->data; + printf("%-15s", k->ifname); + printf("%-15s", k->flags & IFF_UP ? "UP" : ""); + switch (k->media_type) { + case IFT_ETHER: + ifms_type = IFM_ETHER; + break; + case IFT_FDDI: + ifms_type = IFM_FDDI; + break; + case IFT_CARP: + ifms_type = IFM_CARP; + break; + default: + ifms_type = 0; + break; + } + + if (ifms_type) + printf("%s, %s", get_media_descr(ifms_type), + get_linkstate(ifms_type, k->link_state)); + else if (k->link_state == LINK_STATE_UNKNOWN) + printf("unknown"); + else + printf("link state %u", k->link_state); + + if (k->link_state != LINK_STATE_DOWN && k->baudrate > 0) { + printf(", "); + print_baudrate(k->baudrate); + } + printf("\n"); + break; + case IMSG_CTL_END: + printf("\n"); + return (1); + default: + break; + } + + return (0); +} + +const int ifm_status_valid_list[] = IFM_STATUS_VALID_LIST; +const struct ifmedia_status_description + ifm_status_descriptions[] = IFM_STATUS_DESCRIPTIONS; +const struct ifmedia_description + ifm_type_descriptions[] = IFM_TYPE_DESCRIPTIONS; + +const char * +get_media_descr(int media_type) +{ + const struct ifmedia_description *p; + + for (p = ifm_type_descriptions; p->ifmt_string != NULL; p++) + if (media_type == p->ifmt_word) + return (p->ifmt_string); + + return ("unknown"); +} + +const char * +get_linkstate(int media_type, int link_state) +{ + const struct ifmedia_status_description *p; + int i; + + if (link_state == LINK_STATE_UNKNOWN) + return ("unknown"); + + for (i = 0; ifm_status_valid_list[i] != 0; i++) + for (p = ifm_status_descriptions; p->ifms_valid != 0; p++) { + if (p->ifms_type != media_type || + p->ifms_valid != ifm_status_valid_list[i]) + continue; + if (LINK_STATE_IS_UP(link_state)) + return (p->ifms_string[1]); + return (p->ifms_string[0]); + } + + return ("unknown link state"); +} + +void +print_baudrate(u_int64_t baudrate) +{ + if (baudrate > IF_Gbps(1)) + printf("%llu GBit/s", baudrate / IF_Gbps(1)); + else if (baudrate > IF_Mbps(1)) + printf("%llu MBit/s", baudrate / IF_Mbps(1)); + else if (baudrate > IF_Kbps(1)) + printf("%llu KBit/s", baudrate / IF_Kbps(1)); + else + printf("%llu Bit/s", baudrate); +} diff --git a/usr.sbin/ldpctl/parser.c b/usr.sbin/ldpctl/parser.c new file mode 100644 index 00000000000..6285a3c4945 --- /dev/null +++ b/usr.sbin/ldpctl/parser.c @@ -0,0 +1,305 @@ +/* $OpenBSD: parser.c,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * Copyright (c) 2004 Esben Norby <norby@openbsd.org> + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <err.h> +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "ldpd.h" + +#include "parser.h" + +enum token_type { + NOTOKEN, + ENDTOKEN, + KEYWORD, + ADDRESS, + FLAG, + PREFIX, + IFNAME +}; + +struct token { + enum token_type type; + const char *keyword; + int value; + const struct token *next; +}; + +static const struct token t_main[]; +static const struct token t_lfib[]; +static const struct token t_show[]; +static const struct token t_show_iface[]; +static const struct token t_show_db[]; +static const struct token t_show_area[]; +static const struct token t_show_nbr[]; +static const struct token t_show_lib[]; +static const struct token t_show_lfib[]; + +static const struct token t_main[] = { +/* {KEYWORD, "reload", RELOAD, NULL}, */ + {KEYWORD, "lfib", LFIB, t_lfib}, + {KEYWORD, "show", SHOW, t_show}, + {ENDTOKEN, "", NONE, NULL} +}; + +static const struct token t_lfib[] = { + { KEYWORD, "couple", LFIB_COUPLE, NULL}, + { KEYWORD, "decouple", LFIB_DECOUPLE, NULL}, + { ENDTOKEN, "", NONE, NULL} +}; + +static const struct token t_show[] = { + {NOTOKEN, "", NONE, NULL}, + {KEYWORD, "interfaces", SHOW_IFACE, t_show_iface}, + {KEYWORD, "neighbor", SHOW_NBR, t_show_nbr}, + {KEYWORD, "lib", SHOW_LIB, t_show_lib}, + {KEYWORD, "lfib", SHOW_LFIB, t_show_lfib}, + {ENDTOKEN, "", NONE, NULL} +}; + +static const struct token t_show_iface[] = { + {NOTOKEN, "", NONE, NULL}, + {ENDTOKEN, "", NONE, NULL} +}; + +static const struct token t_show_nbr[] = { + {NOTOKEN, "", NONE, NULL}, + {ENDTOKEN, "", NONE, NULL} +}; + +static const struct token t_show_lib[] = { + {NOTOKEN, "", NONE, NULL}, + {ENDTOKEN, "", NONE, NULL} +}; + +static const struct token t_show_lfib[] = { + {NOTOKEN, "", NONE, NULL}, + {KEYWORD, "interface", SHOW_LFIB_IFACE, t_show_iface}, + {FLAG, "connected", F_CONNECTED, t_show_lfib}, + {FLAG, "static", F_STATIC, t_show_lfib}, + {ADDRESS, "", NONE, NULL}, + {ENDTOKEN, "", NONE, NULL} +}; + +static struct parse_result res; + +struct parse_result * +parse(int argc, char *argv[]) +{ + const struct token *table = t_main; + const struct token *match; + + bzero(&res, sizeof(res)); + + while (argc >= 0) { + if ((match = match_token(argv[0], table)) == NULL) { + fprintf(stderr, "valid commands/args:\n"); + show_valid_args(table); + return (NULL); + } + + argc--; + argv++; + + if (match->type == NOTOKEN || match->next == NULL) + break; + + table = match->next; + } + + if (argc > 0) { + fprintf(stderr, "superfluous argument: %s\n", argv[0]); + return (NULL); + } + + return (&res); +} + +const struct token * +match_token(const char *word, const struct token table[]) +{ + u_int i, match; + const struct token *t = NULL; + + match = 0; + + for (i = 0; table[i].type != ENDTOKEN; i++) { + switch (table[i].type) { + case NOTOKEN: + if (word == NULL || strlen(word) == 0) { + match++; + t = &table[i]; + } + break; + case KEYWORD: + if (word != NULL && strncmp(word, table[i].keyword, + strlen(word)) == 0) { + match++; + t = &table[i]; + if (t->value) + res.action = t->value; + } + break; + case FLAG: + if (word != NULL && strncmp(word, table[i].keyword, + strlen(word)) == 0) { + match++; + t = &table[i]; + res.flags |= t->value; + } + break; + case ADDRESS: + if (parse_addr(word, &res.addr)) { + match++; + t = &table[i]; + if (t->value) + res.action = t->value; + } + break; + case PREFIX: + if (parse_prefix(word, &res.addr, &res.prefixlen)) { + match++; + t = &table[i]; + if (t->value) + res.action = t->value; + } + break; + case IFNAME: + if (!match && word != NULL && strlen(word) > 0) { + if (strlcpy(res.ifname, word, + sizeof(res.ifname)) >= + sizeof(res.ifname)) + err(1, "interface name too long"); + match++; + t = &table[i]; + if (t->value) + res.action = t->value; + } + break; + + case ENDTOKEN: + break; + } + } + + if (match != 1) { + if (word == NULL) + fprintf(stderr, "missing argument:\n"); + else if (match > 1) + fprintf(stderr, "ambiguous argument: %s\n", word); + else if (match < 1) + fprintf(stderr, "unknown argument: %s\n", word); + return (NULL); + } + + return (t); +} + +void +show_valid_args(const struct token table[]) +{ + int i; + + for (i = 0; table[i].type != ENDTOKEN; i++) { + switch (table[i].type) { + case NOTOKEN: + fprintf(stderr, " <cr>\n"); + break; + case KEYWORD: + case FLAG: + fprintf(stderr, " %s\n", table[i].keyword); + break; + case ADDRESS: + fprintf(stderr, " <address>\n"); + break; + case PREFIX: + fprintf(stderr, " <address>[/<len>]\n"); + break; + case IFNAME: + fprintf(stderr, " <interface>\n"); + case ENDTOKEN: + break; + } + } +} + +int +parse_addr(const char *word, struct in_addr *addr) +{ + struct in_addr ina; + + if (word == NULL) + return (0); + + bzero(addr, sizeof(struct in_addr)); + bzero(&ina, sizeof(ina)); + + if (inet_pton(AF_INET, word, &ina)) { + addr->s_addr = ina.s_addr; + return (1); + } + + return (0); +} + +int +parse_prefix(const char *word, struct in_addr *addr, u_int8_t *prefixlen) +{ + struct in_addr ina; + int bits = 32; + + if (word == NULL) + return (0); + + bzero(addr, sizeof(struct in_addr)); + bzero(&ina, sizeof(ina)); + + if (strrchr(word, '/') != NULL) { + if ((bits = inet_net_pton(AF_INET, word, + &ina, sizeof(ina))) == -1) + return (0); + addr->s_addr = ina.s_addr & htonl(prefixlen2mask(bits)); + *prefixlen = bits; + return (1); + } else { + *prefixlen = 32; + return (parse_addr(word, addr)); + } + + return (0); +} + +/* XXX local copy from kroute.c, should go to shared file */ +in_addr_t +prefixlen2mask(u_int8_t prefixlen) +{ + if (prefixlen == 0) + return (0); + + return (0xffffffff << (32 - prefixlen)); +} diff --git a/usr.sbin/ldpctl/parser.h b/usr.sbin/ldpctl/parser.h new file mode 100644 index 00000000000..b05a9a7af23 --- /dev/null +++ b/usr.sbin/ldpctl/parser.h @@ -0,0 +1,57 @@ +/* $OpenBSD: parser.h,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * Copyright (c) 2004 Esben Norby <norby@openbsd.org> + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _PARSER_H_ +#define _PARSER_H_ + +#include <sys/types.h> +#include <net/if.h> +#include <netinet/in.h> + +enum actions { + NONE, + LFIB, + LFIB_COUPLE, + LFIB_DECOUPLE, + SHOW, + SHOW_IFACE, + SHOW_NBR, + SHOW_LIB, + SHOW_LFIB, + SHOW_LFIB_IFACE, + RELOAD +}; + +struct parse_result { + struct in_addr addr; + char ifname[IF_NAMESIZE]; + int flags; + enum actions action; + u_int8_t prefixlen; +}; + +struct parse_result *parse(int, char *[]); +const struct token *match_token(const char *, const struct token []); +void show_valid_args(const struct token []); +int parse_addr(const char *, struct in_addr *); +int parse_prefix(const char *, struct in_addr *, + u_int8_t *); + +#endif /* _PARSER_H_ */ diff --git a/usr.sbin/ldpd/Makefile b/usr.sbin/ldpd/Makefile new file mode 100644 index 00000000000..93ecc267177 --- /dev/null +++ b/usr.sbin/ldpd/Makefile @@ -0,0 +1,19 @@ +# $OpenBSD: Makefile,v 1.1 2009/06/01 20:59:45 michele Exp $ + +PROG= ldpd +SRCS= address.c buffer.c control.c hello.c imsg.c init.c interface.c \ + keepalive.c kroute.c labelmapping.c lde.c lde_lib.c ldpd.c ldpe.c \ + log.c name2id.c neighbor.c notification.c packet.c parse.y printconf.c + +MAN= ldpd.8 ldpd.conf.5 + +CFLAGS+= -Wall -I${.CURDIR} +CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes +CFLAGS+= -Wmissing-declarations +CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual +CFLAGS+= -Wsign-compare +YFLAGS= +LDADD+= -levent +DPADD+= ${LIBEVENT} + +.include <bsd.prog.mk> diff --git a/usr.sbin/ldpd/address.c b/usr.sbin/ldpd/address.c new file mode 100644 index 00000000000..d43bacdf697 --- /dev/null +++ b/usr.sbin/ldpd/address.c @@ -0,0 +1,259 @@ +/* $OpenBSD: address.c,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <net/if_dl.h> +#include <unistd.h> + +#include <errno.h> +#include <event.h> +#include <stdlib.h> +#include <string.h> + +#include "ldpd.h" +#include "ldp.h" +#include "log.h" +#include "ldpe.h" + +extern struct ldpd_conf *leconf; + +void gen_address_list_tlv(struct buf *, struct iface *, u_int16_t); + +int +send_address(struct nbr *nbr, struct iface *iface) +{ + struct buf *buf; + struct iface *niface; + u_int16_t size, iface_count = 0; + + if (nbr->iface->passive) + return (0); + + log_debug("send_address: neighbor ID %s", inet_ntoa(nbr->id)); + + if ((buf = buf_open(LDP_MAX_LEN)) == NULL) + fatal("send_address"); + + /* XXX: multiple address on the same iface? */ + if (iface == NULL) + LIST_FOREACH(niface, &leconf->iface_list, entry) + iface_count++; + else + iface_count = 1; + + size = LDP_HDR_SIZE + sizeof(struct ldp_msg) + + sizeof(struct address_list_tlv) + + iface_count * sizeof(struct in_addr); + + gen_ldp_hdr(buf, nbr->iface, size); + + size -= LDP_HDR_SIZE; + + gen_msg_tlv(buf, MSG_TYPE_ADDR, size); + + size -= sizeof(struct ldp_msg); + + gen_address_list_tlv(buf, iface, size); + + bufferevent_write(nbr->bev, buf->buf, buf->wpos); + buf_free(buf); + + return (0); +} + +int +recv_address(struct nbr *nbr, char *buf, u_int16_t len) +{ + struct ldp_msg *addr; + struct address_list_tlv *alt; + struct in_addr *address; + u_int32_t messageid, addrs_len; + + log_debug("recv_address: neighbor ID %s", inet_ntoa(nbr->id)); + + if (nbr->state != NBR_STA_OPER) + return (-1); + + addr = (struct ldp_msg *)buf; + + if ((len - TLV_HDR_LEN) < ntohs(addr->length)) { + /* XXX: send notification */ + return (-1); + } + + messageid = addr->msgid; + + buf += sizeof(struct ldp_msg); + len -= sizeof(struct ldp_msg); + + alt = (struct address_list_tlv *)buf; + + if (len < sizeof(*alt) || + (len - TLV_HDR_LEN) < ntohs(alt->length)) { + /* XXX: send notification */ + return (-1); + } + + addrs_len = (ntohs(alt->length) - sizeof(alt->family)); + + if (alt->type != TLV_TYPE_ADDRLIST) { + /* XXX: send notification */ + return (-1); + } + + /* For now we only support IPv4 */ + if (alt->family != htons(ADDR_IPV4)) { + /* XXX: send notification */ + return (-1); + } + + buf += sizeof(*alt); + len -= sizeof(*alt); + address = (struct in_addr *)buf; + + while (addrs_len >= sizeof(address)) { + ldpe_imsg_compose_lde(IMSG_ADDRESS_ADD, nbr->peerid, 0, + address, sizeof(*address)); + + address++; + addrs_len -= sizeof(*address); + } + + return (ntohs(addr->length)); +} + +void +gen_address_list_tlv(struct buf *buf, struct iface *iface, u_int16_t size) +{ + struct address_list_tlv alt; + struct iface *niface; + + /* We want just the size of the value */ + size -= TLV_HDR_LEN; + + bzero(&alt, sizeof(alt)); + alt.type = TLV_TYPE_ADDRLIST; + alt.length = htons(size); + /* XXX: just ipv4 for now */ + alt.family = htons(ADDR_IPV4); + + buf_add(buf, &alt, sizeof(alt)); + + if (iface == NULL) + LIST_FOREACH(niface, &leconf->iface_list, entry) + buf_add(buf, &niface->addr, sizeof(niface->addr)); + else + buf_add(buf, &iface->addr, sizeof(iface->addr)); +} + +int +send_address_withdraw(struct nbr *nbr, struct iface *iface) +{ + struct buf *buf; + u_int16_t size; + + if (nbr->iface->passive) + return (0); + + log_debug("send_address_withdraw: neighbor ID %s", inet_ntoa(nbr->id)); + + if ((buf = buf_open(LDP_MAX_LEN)) == NULL) + fatal("send_address_withdraw"); + + /* XXX: multiple address on the same iface? */ + size = LDP_HDR_SIZE + sizeof(struct ldp_msg) + sizeof(struct in_addr); + + gen_ldp_hdr(buf, nbr->iface, size); + + size -= LDP_HDR_SIZE; + + gen_msg_tlv(buf, MSG_TYPE_ADDRWITHDRAW, size); + + size -= sizeof(struct ldp_msg); + + gen_address_list_tlv(buf, iface, size); + + bufferevent_write(nbr->bev, buf->buf, buf->wpos); + buf_free(buf); + + return (0); +} + +int +recv_address_withdraw(struct nbr *nbr, char *buf, u_int16_t len) +{ + struct ldp_msg *aw; + struct address_list_tlv *alt; + struct in_addr *address; + u_int32_t messageid; + + log_debug("recv_address_withdraw: neighbor ID %s", inet_ntoa(nbr->id)); + + aw = (struct ldp_msg *)buf; + + + if ((len - TLV_HDR_LEN) < ntohs(aw->length)) { + /* XXX: send notification */ + return (-1); + } + + messageid = aw->msgid; + + buf += sizeof(struct ldp_msg); + len -= sizeof(struct ldp_msg); + + alt = (struct address_list_tlv *)buf; + + if (len < sizeof(*alt) || + (len - TLV_HDR_LEN) < ntohs(alt->length)) { + /* XXX: send notification */ + return (-1); + } + + if (alt->type != TLV_TYPE_ADDRLIST) { + /* XXX: send notification */ + return (-1); + } + + /* For now we just support IPv4 */ + if (alt->family != AF_INET) { + /* XXX: send notification */ + return (-1); + } + + buf += sizeof(*alt); + len -= sizeof(*alt); + address = (struct in_addr *)buf; + + while (len >= sizeof(address)) { + ldpe_imsg_compose_lde(IMSG_ADDRESS_DEL, nbr->peerid, 0, + address, sizeof(*address)); + + address++; + len -= sizeof(*address); + } + + return (ntohs(aw->length)); +} diff --git a/usr.sbin/ldpd/buffer.c b/usr.sbin/ldpd/buffer.c new file mode 100644 index 00000000000..6e00d182ef3 --- /dev/null +++ b/usr.sbin/ldpd/buffer.c @@ -0,0 +1,218 @@ +/* $OpenBSD: buffer.c,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/uio.h> + +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "ldpd.h" + +int buf_realloc(struct buf *, size_t); +void buf_enqueue(struct msgbuf *, struct buf *); +void buf_dequeue(struct msgbuf *, struct buf *); + +struct buf * +buf_open(size_t len) +{ + struct buf *buf; + + if ((buf = calloc(1, sizeof(struct buf))) == NULL) + return (NULL); + if ((buf->buf = malloc(len)) == NULL) { + free(buf); + return (NULL); + } + buf->size = buf->max = len; + + return (buf); +} + +struct buf * +buf_dynamic(size_t len, size_t max) +{ + struct buf *buf; + + if (max < len) + return (NULL); + + if ((buf = buf_open(len)) == NULL) + return (NULL); + + if (max > 0) + buf->max = max; + + return (buf); +} + +int +buf_realloc(struct buf *buf, size_t len) +{ + u_char *b; + + /* on static buffers max is eq size and so the following fails */ + if (buf->wpos + len > buf->max) { + errno = ENOMEM; + return (-1); + } + + b = realloc(buf->buf, buf->wpos + len); + if (b == NULL) + return (-1); + buf->buf = b; + buf->size = buf->wpos + len; + + return (0); +} + +int +buf_add(struct buf *buf, void *data, size_t len) +{ + if (buf->wpos + len > buf->size) + if (buf_realloc(buf, len) == -1) + return (-1); + + memcpy(buf->buf + buf->wpos, data, len); + buf->wpos += len; + return (0); +} + +void * +buf_reserve(struct buf *buf, size_t len) +{ + void *b; + + if (buf->wpos + len > buf->size) + if (buf_realloc(buf, len) == -1) + return (NULL); + + b = buf->buf + buf->wpos; + buf->wpos += len; + return (b); +} + +void * +buf_seek(struct buf *buf, size_t pos, size_t len) +{ + /* only allowed to seek in already written parts */ + if (pos + len > buf->wpos) + return (NULL); + + return (buf->buf + pos); +} + +int +buf_close(struct msgbuf *msgbuf, struct buf *buf) +{ + buf_enqueue(msgbuf, buf); + return (1); +} + +void +buf_free(struct buf *buf) +{ + free(buf->buf); + free(buf); +} + +void +msgbuf_init(struct msgbuf *msgbuf) +{ + msgbuf->queued = 0; + msgbuf->fd = -1; + TAILQ_INIT(&msgbuf->bufs); +} + +void +msgbuf_clear(struct msgbuf *msgbuf) +{ + struct buf *buf; + + while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL) + buf_dequeue(msgbuf, buf); +} + +int +msgbuf_write(struct msgbuf *msgbuf) +{ + struct iovec iov[IOV_MAX]; + struct buf *buf, *next; + int i = 0; + ssize_t n; + struct msghdr msg; + + bzero(&iov, sizeof(iov)); + bzero(&msg, sizeof(msg)); + TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { + if (i >= IOV_MAX) + break; + iov[i].iov_base = buf->buf + buf->rpos; + iov[i].iov_len = buf->size - buf->rpos; + i++; + } + + msg.msg_iov = iov; + msg.msg_iovlen = i; + + if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) { + if (errno == EAGAIN || errno == ENOBUFS || + errno == EINTR) /* try later */ + return (0); + else + return (-1); + } + + if (n == 0) { /* connection closed */ + errno = 0; + return (-2); + } + + for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0; + buf = next) { + next = TAILQ_NEXT(buf, entry); + if (buf->rpos + n >= buf->size) { + n -= buf->size - buf->rpos; + buf_dequeue(msgbuf, buf); + } else { + buf->rpos += n; + n = 0; + } + } + + return (0); +} + +void +buf_enqueue(struct msgbuf *msgbuf, struct buf *buf) +{ + TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry); + msgbuf->queued++; +} + +void +buf_dequeue(struct msgbuf *msgbuf, struct buf *buf) +{ + TAILQ_REMOVE(&msgbuf->bufs, buf, entry); + msgbuf->queued--; + buf_free(buf); +} diff --git a/usr.sbin/ldpd/control.c b/usr.sbin/ldpd/control.c new file mode 100644 index 00000000000..5da80f884ee --- /dev/null +++ b/usr.sbin/ldpd/control.c @@ -0,0 +1,294 @@ +/* $OpenBSD: control.c,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "ldpd.h" +#include "ldp.h" +#include "ldpe.h" +#include "log.h" +#include "control.h" + +#define CONTROL_BACKLOG 5 + +int control_imsg_relay(struct imsg *imsg); + +struct ctl_conn *control_connbyfd(int); +struct ctl_conn *control_connbypid(pid_t); +void control_close(int); + +int +control_init(void) +{ + struct sockaddr_un sun; + int fd; + mode_t old_umask; + + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + log_warn("control_init: socket"); + return (-1); + } + + bzero(&sun, sizeof(sun)); + sun.sun_family = AF_UNIX; + strlcpy(sun.sun_path, LDPD_SOCKET, sizeof(sun.sun_path)); + + if (unlink(LDPD_SOCKET) == -1) + if (errno != ENOENT) { + log_warn("control_init: unlink %s", LDPD_SOCKET); + close(fd); + return (-1); + } + + old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); + if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) { + log_warn("control_init: bind: %s", LDPD_SOCKET); + close(fd); + umask(old_umask); + return (-1); + } + umask(old_umask); + + if (chmod(LDPD_SOCKET, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) { + log_warn("control_init: chmod"); + close(fd); + (void)unlink(LDPD_SOCKET); + return (-1); + } + + session_socket_blockmode(fd, BM_NONBLOCK); + control_state.fd = fd; + + return (0); +} + +int +control_listen(void) +{ + + if (listen(control_state.fd, CONTROL_BACKLOG) == -1) { + log_warn("control_listen: listen"); + return (-1); + } + + event_set(&control_state.ev, control_state.fd, EV_READ | EV_PERSIST, + control_accept, NULL); + event_add(&control_state.ev, NULL); + + return (0); +} + +void +control_cleanup(void) +{ + unlink(LDPD_SOCKET); +} + +/* ARGSUSED */ +void +control_accept(int listenfd, short event, void *bula) +{ + int connfd; + socklen_t len; + struct sockaddr_un sun; + struct ctl_conn *c; + + len = sizeof(sun); + if ((connfd = accept(listenfd, + (struct sockaddr *)&sun, &len)) == -1) { + if (errno != EWOULDBLOCK && errno != EINTR) + log_warn("control_accept"); + return; + } + + session_socket_blockmode(connfd, BM_NONBLOCK); + + if ((c = malloc(sizeof(struct ctl_conn))) == NULL) { + log_warn("control_accept"); + close(connfd); + return; + } + + imsg_init(&c->ibuf, connfd, control_dispatch_imsg); + c->ibuf.events = EV_READ; + event_set(&c->ibuf.ev, c->ibuf.fd, c->ibuf.events, + c->ibuf.handler, &c->ibuf); + event_add(&c->ibuf.ev, NULL); + + TAILQ_INSERT_TAIL(&ctl_conns, c, entry); +} + +struct ctl_conn * +control_connbyfd(int fd) +{ + struct ctl_conn *c; + + for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->ibuf.fd != fd; + c = TAILQ_NEXT(c, entry)) + ; /* nothing */ + + return (c); +} + +struct ctl_conn * +control_connbypid(pid_t pid) +{ + struct ctl_conn *c; + + for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->ibuf.pid != pid; + c = TAILQ_NEXT(c, entry)) + ; /* nothing */ + + return (c); +} + +void +control_close(int fd) +{ + struct ctl_conn *c; + + if ((c = control_connbyfd(fd)) == NULL) { + log_warn("control_close: fd %d: not found", fd); + return; + } + + msgbuf_clear(&c->ibuf.w); + TAILQ_REMOVE(&ctl_conns, c, entry); + + event_del(&c->ibuf.ev); + close(c->ibuf.fd); + free(c); +} + +/* ARGSUSED */ +void +control_dispatch_imsg(int fd, short event, void *bula) +{ + struct ctl_conn *c; + struct imsg imsg; + int n; + unsigned int ifidx; + + if ((c = control_connbyfd(fd)) == NULL) { + log_warn("control_dispatch_imsg: fd %d: not found", fd); + return; + } + + if (event & EV_READ) { + if ((n = imsg_read(&c->ibuf)) == -1 || n == 0) { + control_close(fd); + return; + } + } + if (event & EV_WRITE) { + if (msgbuf_write(&c->ibuf.w) == -1) { + control_close(fd); + return; + } + } + + for (;;) { + if ((n = imsg_get(&c->ibuf, &imsg)) == -1) { + control_close(fd); + return; + } + + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_CTL_LFIB_COUPLE: + case IMSG_CTL_LFIB_DECOUPLE: + ldpe_fib_update(imsg.hdr.type); + /* FALLTHROUGH */ + case IMSG_CTL_RELOAD: + c->ibuf.pid = imsg.hdr.pid; + ldpe_imsg_compose_parent(imsg.hdr.type, 0, NULL, 0); + break; + case IMSG_CTL_KROUTE: + case IMSG_CTL_KROUTE_ADDR: + case IMSG_CTL_IFINFO: + c->ibuf.pid = imsg.hdr.pid; + ldpe_imsg_compose_parent(imsg.hdr.type, + imsg.hdr.pid, imsg.data, + imsg.hdr.len - IMSG_HEADER_SIZE); + break; + case IMSG_CTL_SHOW_INTERFACE: + if (imsg.hdr.len == IMSG_HEADER_SIZE + + sizeof(ifidx)) { + memcpy(&ifidx, imsg.data, sizeof(ifidx)); + ldpe_iface_ctl(c, ifidx); + imsg_compose(&c->ibuf, IMSG_CTL_END, 0, + 0, NULL, 0); + } + break; + case IMSG_CTL_SHOW_LIB: + c->ibuf.pid = imsg.hdr.pid; + ldpe_imsg_compose_lde(imsg.hdr.type, 0, imsg.hdr.pid, + imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); + break; + case IMSG_CTL_SHOW_NBR: + ldpe_nbr_ctl(c); + break; + default: + log_debug("control_dispatch_imsg: " + "error handling imsg %d", imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + + imsg_event_add(&c->ibuf); +} + +int +control_imsg_relay(struct imsg *imsg) +{ + struct ctl_conn *c; + + if ((c = control_connbypid(imsg->hdr.pid)) == NULL) + return (0); + + return (imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid, + imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE)); +} + +void +session_socket_blockmode(int fd, enum blockmodes bm) +{ + int flags; + + if ((flags = fcntl(fd, F_GETFL, 0)) == -1) + fatal("fcntl F_GETFL"); + + if (bm == BM_NONBLOCK) + flags |= O_NONBLOCK; + else + flags &= ~O_NONBLOCK; + + if ((flags = fcntl(fd, F_SETFL, flags)) == -1) + fatal("fcntl F_SETFL"); +} diff --git a/usr.sbin/ldpd/control.h b/usr.sbin/ldpd/control.h new file mode 100644 index 00000000000..30de3762269 --- /dev/null +++ b/usr.sbin/ldpd/control.h @@ -0,0 +1,50 @@ +/* $OpenBSD: control.h,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _CONTROL_H_ +#define _CONTROL_H_ + +#include <sys/queue.h> +#include <sys/time.h> +#include <event.h> + +struct { + struct event ev; + int fd; +} control_state; + +enum blockmodes { + BM_NORMAL, + BM_NONBLOCK +}; + +struct ctl_conn { + TAILQ_ENTRY(ctl_conn) entry; + struct imsgbuf ibuf; +}; + +int control_init(void); +int control_listen(void); +void control_accept(int, short, void *); +void control_dispatch_imsg(int, short, void *); +int control_imsg_relay(struct imsg *); +void control_cleanup(void); + +void session_socket_blockmode(int, enum blockmodes); + +#endif /* _CONTROL_H_ */ diff --git a/usr.sbin/ldpd/hello.c b/usr.sbin/ldpd/hello.c new file mode 100644 index 00000000000..3363af68983 --- /dev/null +++ b/usr.sbin/ldpd/hello.c @@ -0,0 +1,195 @@ +/* $OpenBSD: hello.c,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <net/if_dl.h> + +#include <errno.h> +#include <event.h> +#include <stdlib.h> +#include <string.h> + +#include "ldpd.h" +#include "ldp.h" +#include "log.h" +#include "ldpe.h" + +struct hello_prms_tlv *tlv_decode_hello_prms(char *, u_int16_t); +int tlv_decode_opt_hello_prms(char *, u_int16_t); +int gen_hello_prms_tlv(struct iface *, struct buf *, + u_int16_t); + +int +send_hello(struct iface *iface) +{ + struct sockaddr_in dst; + struct buf *buf; + u_int16_t size; + + dst.sin_port = htons(LDP_PORT); + dst.sin_family = AF_INET; + dst.sin_len = sizeof(struct sockaddr_in); + inet_aton(AllRouters, &dst.sin_addr); + + if (iface->passive) + return (0); + + log_debug("send_hello: iface %s", iface->name); + + if ((buf = buf_open(LDP_MAX_LEN)) == NULL) + fatal("send_hello"); + + size = LDP_HDR_SIZE + sizeof(struct ldp_msg) + + sizeof(struct hello_prms_tlv); + + gen_ldp_hdr(buf, iface, size); + + size -= LDP_HDR_SIZE; + + gen_msg_tlv(buf, MSG_TYPE_HELLO, size); + + size -= sizeof(struct ldp_msg); + + gen_hello_prms_tlv(iface, buf, size); + + send_packet(iface, buf->buf, buf->wpos, &dst); + buf_free(buf); + + return (0); +} + +void +recv_hello(struct iface *iface, struct in_addr src, char *buf, u_int16_t len) +{ + struct ldp_msg *hello; + u_int32_t messageid; + struct nbr *nbr = NULL; + struct hello_prms_tlv *cpt; + struct ldp_hdr *ldp; + + log_debug("recv_hello: neighbor %s", inet_ntoa(src)); + + ldp = (struct ldp_hdr *)buf; + + buf += LDP_HDR_SIZE; + len -= LDP_HDR_SIZE; + + hello = (struct ldp_msg *)buf; + + if ((len - TLV_HDR_LEN) < ntohs(hello->length)) { + /* XXX: send notification */ + return; + } + + messageid = hello->msgid; + + buf += sizeof(struct ldp_msg); + len -= sizeof(struct ldp_msg); + + cpt = tlv_decode_hello_prms(buf, len); + if (cpt == NULL) + return; + + buf += sizeof(struct hello_prms_tlv); + len -= sizeof(struct hello_prms_tlv); + + tlv_decode_opt_hello_prms(buf, len); + + nbr = nbr_find_ldpid(iface, ldp->lsr_id, ldp->lspace_id); + if (!nbr) { + nbr = nbr_new(ldp->lsr_id, ldp->lspace_id, iface, 0); + + /* set neighbor parameters */ + nbr->addr.s_addr = src.s_addr; + nbr->hello_type = cpt->reserved; + + if (cpt->holdtime == 0) { + /* XXX: lacks support for targeted hellos */ + if (iface->holdtime < LINK_DFLT_HOLDTIME) + nbr->holdtime = iface->holdtime; + else + nbr->holdtime = LINK_DFLT_HOLDTIME; + } else if (cpt->holdtime == INFINITE_HOLDTIME) { + /* No timeout for this neighbor */ + nbr->holdtime = iface->holdtime; + } else { + if (iface->holdtime < ntohs(cpt->holdtime)) + nbr->holdtime = iface->holdtime; + else + nbr->holdtime = ntohs(cpt->holdtime); + } + } + + nbr_fsm(nbr, NBR_EVT_HELLO_RCVD); + + if (nbr->addr.s_addr < nbr->iface->addr.s_addr && + nbr->state == NBR_STA_PRESENT && !nbr_pending_idtimer(nbr)) + nbr_act_session_establish(nbr, 1); +} + +int +gen_hello_prms_tlv(struct iface *iface, struct buf *buf, u_int16_t size) +{ + struct hello_prms_tlv parms; + + /* We want just the size of the value */ + size -= TLV_HDR_LEN; + + bzero(&parms, sizeof(parms)); + parms.type = htons(TLV_TYPE_COMMONHELLO); + parms.length = htons(size); + /* XXX */ + parms.holdtime = htons(iface->holdtime); + parms.reserved = 0; + + return (buf_add(buf, &parms, sizeof(parms))); +} + +struct hello_prms_tlv * +tlv_decode_hello_prms(char *buf, u_int16_t len) +{ + struct hello_prms_tlv *tlv; + + tlv = (struct hello_prms_tlv *)buf; + + if (len < sizeof(*tlv) || ntohs(tlv->length) != + (sizeof(tlv->holdtime) + sizeof(tlv->reserved))) + return (NULL); + + if ((tlv->type & ~UNKNOWN_FLAGS_MASK) != htons(TLV_TYPE_COMMONHELLO)) + return (NULL); + + if ((tlv->type & UNKNOWN_FLAGS_MASK) != 0) + return (NULL); + + return (tlv); +} + +int +tlv_decode_opt_hello_prms(char *buf, u_int16_t len) +{ + /* XXX: todo */ + return (0); +} diff --git a/usr.sbin/ldpd/imsg.c b/usr.sbin/ldpd/imsg.c new file mode 100644 index 00000000000..20724baed2e --- /dev/null +++ b/usr.sbin/ldpd/imsg.c @@ -0,0 +1,181 @@ +/* $OpenBSD: imsg.c,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/uio.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "ldpd.h" +#include "log.h" + +void +imsg_init(struct imsgbuf *ibuf, int fd, void (*handler)(int, short, void *)) +{ + msgbuf_init(&ibuf->w); + bzero(&ibuf->r, sizeof(ibuf->r)); + ibuf->fd = fd; + ibuf->w.fd = fd; + ibuf->pid = getpid(); + ibuf->handler = handler; + TAILQ_INIT(&ibuf->fds); +} + +ssize_t +imsg_read(struct imsgbuf *ibuf) +{ + ssize_t n; + + if ((n = recv(ibuf->fd, ibuf->r.buf + ibuf->r.wpos, + sizeof(ibuf->r.buf) - ibuf->r.wpos, 0)) == -1) { + if (errno != EINTR && errno != EAGAIN) { + log_warn("imsg_read: pipe read error"); + return (-1); + } + return (-2); + } + + ibuf->r.wpos += n; + + return (n); +} + +ssize_t +imsg_get(struct imsgbuf *ibuf, struct imsg *imsg) +{ + size_t av, left, datalen; + + av = ibuf->r.wpos; + + if (IMSG_HEADER_SIZE > av) + return (0); + + memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr)); + if (imsg->hdr.len < IMSG_HEADER_SIZE || + imsg->hdr.len > MAX_IMSGSIZE) { + log_warnx("imsg_get: imsg hdr len %u out of bounds, type=%u", + imsg->hdr.len, imsg->hdr.type); + return (-1); + } + if (imsg->hdr.len > av) + return (0); + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; + ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE; + if ((imsg->data = malloc(datalen)) == NULL) { + log_warn("imsg_get"); + return (-1); + } + memcpy(imsg->data, ibuf->r.rptr, datalen); + + if (imsg->hdr.len < av) { + left = av - imsg->hdr.len; + memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left); + ibuf->r.wpos = left; + } else + ibuf->r.wpos = 0; + + return (datalen + IMSG_HEADER_SIZE); +} + +int +imsg_compose(struct imsgbuf *ibuf, enum imsg_type type, u_int32_t peerid, + pid_t pid, void *data, u_int16_t datalen) +{ + struct buf *wbuf; + int n; + + if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL) + return (-1); + + if (imsg_add(wbuf, data, datalen) == -1) + return (-1); + + if ((n = imsg_close(ibuf, wbuf)) < 0) + return (-1); + + return (n); +} + +/* ARGSUSED */ +struct buf * +imsg_create(struct imsgbuf *ibuf, enum imsg_type type, u_int32_t peerid, + pid_t pid, u_int16_t datalen) +{ + struct buf *wbuf; + struct imsg_hdr hdr; + + datalen += IMSG_HEADER_SIZE; + if (datalen > MAX_IMSGSIZE) { + log_warnx("imsg_create: len %u > MAX_IMSGSIZE; " + "type %u peerid %lu", datalen + IMSG_HEADER_SIZE, + type, peerid); + return (NULL); + } + + hdr.type = type; + hdr.peerid = peerid; + if ((hdr.pid = pid) == 0) + hdr.pid = ibuf->pid; + if ((wbuf = buf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) { + log_warn("imsg_create: buf_open"); + return (NULL); + } + if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1) + return (NULL); + + return (wbuf); +} + +int +imsg_add(struct buf *msg, void *data, u_int16_t datalen) +{ + if (datalen) + if (buf_add(msg, data, datalen) == -1) { + log_warnx("imsg_add: buf_add error"); + buf_free(msg); + return (-1); + } + return (datalen); +} + +int +imsg_close(struct imsgbuf *ibuf, struct buf *msg) +{ + int n; + struct imsg_hdr *hdr; + + hdr = (struct imsg_hdr *)msg->buf; + hdr->len = (u_int16_t)msg->wpos; + if ((n = buf_close(&ibuf->w, msg)) < 0) { + log_warnx("imsg_close: buf_close error"); + buf_free(msg); + return (-1); + } + imsg_event_add(ibuf); + + return (n); +} + +void +imsg_free(struct imsg *imsg) +{ + free(imsg->data); +} diff --git a/usr.sbin/ldpd/init.c b/usr.sbin/ldpd/init.c new file mode 100644 index 00000000000..68667108b73 --- /dev/null +++ b/usr.sbin/ldpd/init.c @@ -0,0 +1,134 @@ +/* $OpenBSD: init.c,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <net/if_dl.h> +#include <unistd.h> + +#include <errno.h> +#include <event.h> +#include <stdlib.h> +#include <string.h> + +#include "ldpd.h" +#include "ldp.h" +#include "log.h" +#include "ldpe.h" + +int gen_init_prms_tlv(struct buf *, struct nbr *, u_int16_t); + +int +send_init(struct nbr *nbr) +{ + struct buf *buf; + u_int16_t size; + + if (nbr->iface->passive) + return (0); + + log_debug("send_init: neighbor ID %s", inet_ntoa(nbr->id)); + + if ((buf = buf_open(LDP_MAX_LEN)) == NULL) + fatal("send_init"); + + size = LDP_HDR_SIZE + sizeof(struct ldp_msg) + SESS_PRMS_SIZE; + + gen_ldp_hdr(buf, nbr->iface, size); + + size -= LDP_HDR_SIZE; + + gen_msg_tlv(buf, MSG_TYPE_INIT, size); + + size -= sizeof(struct ldp_msg); + + gen_init_prms_tlv(buf, nbr, size); + + bufferevent_write(nbr->bev, buf->buf, buf->wpos); + buf_free(buf); + + return (0); +} + +int +recv_init(struct nbr *nbr, char *buf, u_int16_t len) +{ + struct ldp_msg *init; + u_int32_t messageid; + struct sess_prms_tlv *sess_tlv; + + log_debug("recv_init: neighbor ID %s", inet_ntoa(nbr->id)); + + init = (struct ldp_msg *)buf; + + if ((len - TLV_HDR_LEN) < ntohs(init->length)) { + /* XXX: send notification */ + return (-1); + } + + messageid = init->msgid; + + buf += sizeof(struct ldp_msg); + len -= sizeof(struct ldp_msg); + + sess_tlv = (struct sess_prms_tlv *)buf; + + if (len < SESS_PRMS_SIZE || + ntohs(sess_tlv->length) != (SESS_PRMS_SIZE - TLV_HDR_LEN)) { + /* XXX: send notification */ + return (-1); + } + + if (nbr->iface->keepalive < ntohs(sess_tlv->keepalive_time)) + nbr->keepalive = nbr->iface->keepalive; + else + nbr->keepalive = ntohs(sess_tlv->keepalive_time); + + nbr_fsm(nbr, NBR_EVT_INIT_RCVD); + + return (ntohs(init->length)); +} + +int +gen_init_prms_tlv(struct buf *buf, struct nbr *nbr, u_int16_t size) +{ + struct sess_prms_tlv parms; + + /* We want just the size of the value */ + size -= TLV_HDR_LEN; + + bzero(&parms, sizeof(parms)); + parms.type = htons(TLV_TYPE_COMMONSESSION); + parms.length = htons(size); + parms.proto_version = htons(LDP_VERSION); + parms.keepalive_time = htons(nbr->iface->keepalive); + parms.reserved = 0; + parms.pvlim = 0; + parms.max_pdu_len = 0; + parms.lsr_id = nbr->id.s_addr; + /* XXX: nbr lspace */ + parms.lspace_id = 0; + + return (buf_add(buf, &parms, SESS_PRMS_SIZE)); +} diff --git a/usr.sbin/ldpd/interface.c b/usr.sbin/ldpd/interface.c new file mode 100644 index 00000000000..b921a5d675a --- /dev/null +++ b/usr.sbin/ldpd/interface.c @@ -0,0 +1,603 @@ +/* $OpenBSD: interface.c,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <net/if.h> +#include <net/if_types.h> +#include <fcntl.h> +#include <ctype.h> +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <event.h> + +#include "ldpd.h" +#include "ldp.h" +#include "log.h" +#include "ldpe.h" + +void if_hello_timer(int, short, void *); +void if_start_hello_timer(struct iface *); +void if_stop_hello_timer(struct iface *); +struct nbr *if_elect(struct nbr *, struct nbr *); + +struct { + int state; + enum iface_event event; + enum iface_action action; + int new_state; +} iface_fsm[] = { + /* current state event that happened action to take resulting state */ + {IF_STA_DOWN, IF_EVT_UP, IF_ACT_STRT, IF_STA_ACTIVE}, + {IF_STA_ANY, IF_EVT_DOWN, IF_ACT_RST, IF_STA_DOWN}, + {-1, IF_EVT_NOTHING, IF_ACT_NOTHING, 0}, +}; + +static int vlink_cnt = 0; + +const char * const if_event_names[] = { + "NOTHING", + "UP", + "DOWN" +}; + +const char * const if_action_names[] = { + "NOTHING", + "START", + "RESET" +}; + +int +if_fsm(struct iface *iface, enum iface_event event) +{ + int old_state; + int new_state = 0; + int i, ret = 0; + + old_state = iface->state; + + for (i = 0; iface_fsm[i].state != -1; i++) + if ((iface_fsm[i].state & old_state) && + (iface_fsm[i].event == event)) { + new_state = iface_fsm[i].new_state; + break; + } + + if (iface_fsm[i].state == -1) { + /* event outside of the defined fsm, ignore it. */ + log_debug("if_fsm: interface %s, " + "event %s not expected in state %s", iface->name, + if_event_names[event], if_state_name(old_state)); + return (0); + } + + switch (iface_fsm[i].action) { + case IF_ACT_STRT: + ret = if_act_start(iface); + break; + case IF_ACT_RST: + ret = if_act_reset(iface); + break; + case IF_ACT_NOTHING: + /* do nothing */ + break; + } + + if (ret) { + log_debug("if_fsm: error changing state for interface %s, " + "event %s, state %s", iface->name, if_event_names[event], + if_state_name(old_state)); + return (-1); + } + + if (new_state != 0) + iface->state = new_state; + + log_debug("if_fsm: event %s resulted in action %s and changing " + "state for interface %s from %s to %s", + if_event_names[event], if_action_names[iface_fsm[i].action], + iface->name, if_state_name(old_state), if_state_name(iface->state)); + + return (ret); +} + +struct iface * +if_new(struct kif *kif, struct kif_addr *ka) +{ + struct iface *iface; + + if ((iface = calloc(1, sizeof(*iface))) == NULL) + err(1, "if_new: calloc"); + + iface->state = IF_STA_DOWN; + + LIST_INIT(&iface->nbr_list); + LIST_INIT(&iface->lde_nbr_list); + + if (kif == NULL) { + iface->type = IF_TYPE_VIRTUALLINK; + snprintf(iface->name, sizeof(iface->name), "vlink%d", + vlink_cnt++); + iface->flags |= IFF_UP; + iface->mtu = IP_MSS; + return (iface); + } + + strlcpy(iface->name, kif->ifname, sizeof(iface->name)); + + /* get type */ + if (kif->flags & IFF_POINTOPOINT) + iface->type = IF_TYPE_POINTOPOINT; + if (kif->flags & IFF_BROADCAST && + kif->flags & IFF_MULTICAST) + iface->type = IF_TYPE_BROADCAST; + if (kif->flags & IFF_LOOPBACK) { + iface->type = IF_TYPE_POINTOPOINT; + iface->state = IF_STA_LOOPBACK; + } + + /* get mtu, index and flags */ + iface->mtu = kif->mtu; + iface->ifindex = kif->ifindex; + iface->flags = kif->flags; + iface->linkstate = kif->link_state; + iface->media_type = kif->media_type; + iface->baudrate = kif->baudrate; + + /* set address, mask and p2p addr */ + iface->addr = ka->addr; + iface->mask = ka->mask; + if (kif->flags & IFF_POINTOPOINT) { + iface->dst = ka->dstbrd; + } + + return (iface); +} + +void +if_del(struct iface *iface) +{ + struct nbr *nbr = NULL; + + log_debug("if_del: interface %s", iface->name); + + /* clear lists etc */ + while ((nbr = LIST_FIRST(&iface->nbr_list)) != NULL) + nbr_del(nbr); + + if (evtimer_pending(&iface->hello_timer, NULL)) + evtimer_del(&iface->hello_timer); + + free(iface); +} + +void +if_init(struct ldpd_conf *xconf, struct iface *iface) +{ + /* init the dummy local neighbor */ + iface->self = nbr_new(ldpe_router_id(), iface->ifindex, iface, 1); + + /* set event handlers for interface */ + evtimer_set(&iface->hello_timer, if_hello_timer, iface); + + iface->discovery_fd = xconf->ldp_discovery_socket; +} + +/* timers */ +/* ARGSUSED */ +void +if_hello_timer(int fd, short event, void *arg) +{ + struct iface *iface = arg; + struct timeval tv; + + send_hello(iface); + + /* reschedule hello_timer */ + timerclear(&tv); + tv.tv_sec = iface->hello_interval; + if (evtimer_add(&iface->hello_timer, &tv) == -1) + fatal("if_hello_timer"); +} + +void +if_start_hello_timer(struct iface *iface) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = iface->hello_interval; + if (evtimer_add(&iface->hello_timer, &tv) == -1) + fatal("if_start_hello_timer"); +} + +void +if_stop_hello_timer(struct iface *iface) +{ + if (evtimer_del(&iface->hello_timer) == -1) + fatal("if_stop_hello_timer"); +} + +/* actions */ +int +if_act_start(struct iface *iface) +{ + struct in_addr addr; + struct timeval now; + + if (!((iface->flags & IFF_UP) && + (LINK_STATE_IS_UP(iface->linkstate) || + (iface->linkstate == LINK_STATE_UNKNOWN && + iface->media_type != IFT_CARP)))) { + log_debug("if_act_start: interface %s link down", + iface->name); + return (0); + } + + if (iface->media_type == IFT_CARP && iface->passive == 0) { + /* force passive mode on carp interfaces */ + log_warnx("if_act_start: forcing interface %s to passive", + iface->name); + iface->passive = 1; + } + + gettimeofday(&now, NULL); + iface->uptime = now.tv_sec; + + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + inet_aton(AllRouters, &addr); + if (if_join_group(iface, &addr)) + return (-1); + iface->state = IF_STA_POINTTOPOINT; + break; + case IF_TYPE_VIRTUALLINK: + iface->state = IF_STA_POINTTOPOINT; + break; + case IF_TYPE_POINTOMULTIPOINT: + case IF_TYPE_NBMA: + log_debug("if_act_start: type %s not supported, interface %s", + if_type_name(iface->type), iface->name); + return (-1); + case IF_TYPE_BROADCAST: + inet_aton(AllRouters, &addr); + if (if_join_group(iface, &addr)) + return (-1); + iface->state = IF_STA_DOWN; + break; + default: + fatalx("if_act_start: unknown interface type"); + } + + /* hello timer needs to be started in any case */ + if_start_hello_timer(iface); + return (0); +} + +int +if_act_reset(struct iface *iface) +{ +/* struct nbr *nbr = NULL; */ + struct in_addr addr; + + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + case IF_TYPE_BROADCAST: + inet_aton(AllRouters, &addr); + if (if_leave_group(iface, &addr)) { + log_warnx("if_act_reset: error leaving group %s, " + "interface %s", inet_ntoa(addr), iface->name); + } + break; + case IF_TYPE_VIRTUALLINK: + /* nothing */ + break; + case IF_TYPE_NBMA: + case IF_TYPE_POINTOMULTIPOINT: + log_debug("if_act_reset: type %s not supported, interface %s", + if_type_name(iface->type), iface->name); + return (-1); + default: + fatalx("if_act_reset: unknown interface type"); + } +/* + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (nbr_fsm(nbr, NBR_EVT_KILL_NBR)) { + log_debug("if_act_reset: error killing neighbor %s", + inet_ntoa(nbr->id)); + } + } +*/ + return (0); +} + +struct ctl_iface * +if_to_ctl(struct iface *iface) +{ + static struct ctl_iface ictl; + struct timeval tv, now, res; + struct nbr *nbr; + + memcpy(ictl.name, iface->name, sizeof(ictl.name)); + memcpy(&ictl.addr, &iface->addr, sizeof(ictl.addr)); + memcpy(&ictl.mask, &iface->mask, sizeof(ictl.mask)); + ictl.rtr_id.s_addr = ldpe_router_id(); + ictl.ifindex = iface->ifindex; + ictl.state = iface->state; + ictl.mtu = iface->mtu; + ictl.nbr_cnt = 0; + ictl.adj_cnt = 0; + ictl.baudrate = iface->baudrate; + ictl.holdtime = iface->holdtime; + ictl.hello_interval = iface->hello_interval; + ictl.flags = iface->flags; + ictl.type = iface->type; + ictl.linkstate = iface->linkstate; + ictl.mediatype = iface->media_type; + ictl.priority = iface->priority; + ictl.passive = iface->passive; + + gettimeofday(&now, NULL); + if (evtimer_pending(&iface->hello_timer, &tv)) { + timersub(&tv, &now, &res); + ictl.hello_timer = res.tv_sec; + } else + ictl.hello_timer = -1; + + if (iface->state != IF_STA_DOWN) { + ictl.uptime = now.tv_sec - iface->uptime; + } else + ictl.uptime = 0; + + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (nbr == iface->self) + continue; + ictl.nbr_cnt++; + } + + return (&ictl); +} + +/* misc */ +int +if_set_mcast_ttl(int fd, u_int8_t ttl) +{ + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, + (char *)&ttl, sizeof(ttl)) < 0) { + log_warn("if_set_mcast_ttl: error setting " + "IP_MULTICAST_TTL to %d", ttl); + return (-1); + } + + return (0); +} + +int +if_set_tos(int fd, int tos) +{ + if (setsockopt(fd, IPPROTO_IP, IP_TOS, (int *)&tos, sizeof(tos)) < 0) { + log_warn("if_set_tos: error setting IP_TOS to 0x%x", tos); + return (-1); + } + + return (0); +} + +int +if_set_recvif(int fd, int enable) +{ + if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &enable, + sizeof(enable)) < 0) { + log_warn("if_set_recvif: error setting IP_RECVIF"); + return (-1); + } + return (0); +} + +void +if_set_recvbuf(int fd) +{ + int bsize; + + bsize = 65535; + while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize, + sizeof(bsize)) == -1) + bsize /= 2; +} + +int +if_set_reuse(int fd, int enable) +{ + if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &enable, + sizeof(int)) < 0) { + log_warn("if_set_reuse: error setting SO_REUSEPORT"); + return (-1); + } + + return (0); +} + +int +if_set_nonblock(int fd) +{ + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { + log_warn("if_set_nonblock: error setting O_NONBLOCK"); + return (-1); + } + + return (0); +} + + +/* + * only one JOIN or DROP per interface and address is allowed so we need + * to keep track of what is added and removed. + */ +struct if_group_count { + LIST_ENTRY(if_group_count) entry; + struct in_addr addr; + unsigned int ifindex; + int count; +}; + +LIST_HEAD(,if_group_count) ifglist = LIST_HEAD_INITIALIZER(ifglist); + +int +if_join_group(struct iface *iface, struct in_addr *addr) +{ + struct ip_mreq mreq; + struct if_group_count *ifg; + + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + case IF_TYPE_BROADCAST: + LIST_FOREACH(ifg, &ifglist, entry) + if (iface->ifindex == ifg->ifindex && + addr->s_addr == ifg->addr.s_addr) + break; + if (ifg == NULL) { + if ((ifg = calloc(1, sizeof(*ifg))) == NULL) + fatal("if_join_group"); + ifg->addr.s_addr = addr->s_addr; + ifg->ifindex = iface->ifindex; + LIST_INSERT_HEAD(&ifglist, ifg, entry); + } + + if (ifg->count++ != 0) + /* already joined */ + return (0); + + mreq.imr_multiaddr.s_addr = addr->s_addr; + mreq.imr_interface.s_addr = iface->addr.s_addr; + + if (setsockopt(iface->discovery_fd, IPPROTO_IP, + IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) { + log_warn("if_join_group: error IP_ADD_MEMBERSHIP, " + "interface %s address %s", iface->name, + inet_ntoa(*addr)); + return (-1); + } + break; + case IF_TYPE_POINTOMULTIPOINT: + case IF_TYPE_VIRTUALLINK: + case IF_TYPE_NBMA: + log_debug("if_join_group: type %s not supported, interface %s", + if_type_name(iface->type), iface->name); + return (-1); + default: + fatalx("if_join_group: unknown interface type"); + } + + return (0); +} + +int +if_leave_group(struct iface *iface, struct in_addr *addr) +{ + struct ip_mreq mreq; + struct if_group_count *ifg; + + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + case IF_TYPE_BROADCAST: + LIST_FOREACH(ifg, &ifglist, entry) + if (iface->ifindex == ifg->ifindex && + addr->s_addr == ifg->addr.s_addr) + break; + + /* if interface is not found just try to drop membership */ + if (ifg && --ifg->count != 0) + /* others still joined */ + return (0); + + mreq.imr_multiaddr.s_addr = addr->s_addr; + mreq.imr_interface.s_addr = iface->addr.s_addr; + + if (setsockopt(iface->discovery_fd, IPPROTO_IP, + IP_DROP_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) { + log_warn("if_leave_group: error IP_DROP_MEMBERSHIP, " + "interface %s address %s", iface->name, + inet_ntoa(*addr)); + return (-1); + } + + if (ifg) { + LIST_REMOVE(ifg, entry); + free(ifg); + } + break; + case IF_TYPE_POINTOMULTIPOINT: + case IF_TYPE_VIRTUALLINK: + case IF_TYPE_NBMA: + log_debug("if_leave_group: type %s not supported, interface %s", + if_type_name(iface->type), iface->name); + return (-1); + default: + fatalx("if_leave_group: unknown interface type"); + } + + return (0); +} + +int +if_set_mcast(struct iface *iface) +{ + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + case IF_TYPE_BROADCAST: + if (setsockopt(iface->discovery_fd, IPPROTO_IP, IP_MULTICAST_IF, + &iface->addr.s_addr, sizeof(iface->addr.s_addr)) < 0) { + log_debug("if_set_mcast: error setting " + "IP_MULTICAST_IF, interface %s", iface->name); + return (-1); + } + break; + case IF_TYPE_POINTOMULTIPOINT: + case IF_TYPE_VIRTUALLINK: + case IF_TYPE_NBMA: + log_debug("if_set_mcast: type %s not supported, interface %s", + if_type_name(iface->type), iface->name); + return (-1); + default: + fatalx("if_set_mcast: unknown interface type"); + } + + return (0); +} + +int +if_set_mcast_loop(int fd) +{ + u_int8_t loop = 0; + + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, + (char *)&loop, sizeof(loop)) < 0) { + log_warn("if_set_mcast_loop: error setting IP_MULTICAST_LOOP"); + return (-1); + } + + return (0); +} diff --git a/usr.sbin/ldpd/keepalive.c b/usr.sbin/ldpd/keepalive.c new file mode 100644 index 00000000000..e4ab62c22f2 --- /dev/null +++ b/usr.sbin/ldpd/keepalive.c @@ -0,0 +1,94 @@ +/* $OpenBSD: keepalive.c,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <net/if_dl.h> +#include <unistd.h> + +#include <errno.h> +#include <event.h> +#include <stdlib.h> +#include <string.h> + +#include "ldpd.h" +#include "ldp.h" +#include "log.h" +#include "ldpe.h" + +int +send_keepalive(struct nbr *nbr) +{ + struct buf *buf; + u_int16_t size; + + if (nbr->iface->passive) + return (0); + + log_debug("send_keepalive: neighbor ID %s", inet_ntoa(nbr->id)); + + if ((buf = buf_open(LDP_MAX_LEN)) == NULL) + fatal("send_keepalive"); + + size = LDP_HDR_SIZE + sizeof(struct ldp_msg); + + gen_ldp_hdr(buf, nbr->iface, size); + + size -= LDP_HDR_SIZE; + + gen_msg_tlv(buf, MSG_TYPE_KEEPALIVE, size); + + bufferevent_write(nbr->bev, buf->buf, buf->wpos); + buf_free(buf); + + return (0); +} + +int +recv_keepalive(struct nbr *nbr, char *buf, u_int16_t len) +{ + struct ldp_msg *ka; + u_int32_t messageid; + + log_debug("recv_keepalive: neighbor ID %s", inet_ntoa(nbr->id)); + + ka = (struct ldp_msg *)buf; + + if ((len - TLV_HDR_LEN) < ntohs(ka->length)) { + /* XXX: send notification */ + return (-1); + } + + messageid = ka->msgid; + + buf += sizeof(struct ldp_msg); + len -= sizeof(struct ldp_msg); + + if (nbr->state != NBR_STA_OPER) + nbr_fsm(nbr, NBR_EVT_KEEPALIVE_RCVD); + else + nbr_fsm(nbr, NBR_EVT_PDU_RCVD); + + return (ntohs(ka->length)); +} diff --git a/usr.sbin/ldpd/kroute.c b/usr.sbin/ldpd/kroute.c new file mode 100644 index 00000000000..ab722b76e0c --- /dev/null +++ b/usr.sbin/ldpd/kroute.c @@ -0,0 +1,1739 @@ +/* $OpenBSD: kroute.c,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * Copyright (c) 2004 Esben Norby <norby@openbsd.org> + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/tree.h> +#include <sys/uio.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/route.h> +#include <netmpls/mpls.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "ldpd.h" +#include "log.h" + +struct { + u_int32_t rtseq; + pid_t pid; + int fib_sync; + int fd; + struct event ev; +} kr_state; + +struct kroute_node { + RB_ENTRY(kroute_node) entry; + struct kroute r; + struct kroute_node *next; +}; + +struct kif_node { + RB_ENTRY(kif_node) entry; + TAILQ_HEAD(, kif_addr) addrs; + struct kif k; +}; + +void kr_redist_remove(struct kroute_node *, struct kroute_node *); +int kr_redist_eval(struct kroute *, struct rroute *); +void kr_redistribute(struct kroute_node *); +int kroute_compare(struct kroute_node *, struct kroute_node *); +int kif_compare(struct kif_node *, struct kif_node *); +int kr_change_fib(struct kroute_node *, struct kroute *, int, int); +int kr_delete_fib(struct kroute_node *); + +struct kroute_node *kroute_find(in_addr_t, u_int8_t); +struct kroute_node *kroute_matchgw(struct kroute_node *, struct in_addr); +int kroute_insert(struct kroute_node *); +int kroute_remove(struct kroute_node *); +void kroute_clear(void); + +struct kif_node *kif_find(u_short); +struct kif_node *kif_insert(u_short); +int kif_remove(struct kif_node *); +void kif_clear(void); +struct kif *kif_update(u_short, int, struct if_data *, + struct sockaddr_dl *); +int kif_validate(u_short); + +struct kroute_node *kroute_match(in_addr_t); + +int protect_lo(void); +u_int8_t prefixlen_classful(in_addr_t); +void get_rtaddrs(int, struct sockaddr *, struct sockaddr **); +void if_change(u_short, int, struct if_data *); +void if_newaddr(u_short, struct sockaddr_in *, struct sockaddr_in *, + struct sockaddr_in *); +void if_announce(void *); + +int send_rtmsg(int, int, struct kroute *); +int send_rtlabelmsg(int, int, struct kroute *, u_int32_t); +int dispatch_rtmsg(void); +int fetchtable(void); +int fetchifs(u_short); + +RB_HEAD(kroute_tree, kroute_node) krt; +RB_PROTOTYPE(kroute_tree, kroute_node, entry, kroute_compare) +RB_GENERATE(kroute_tree, kroute_node, entry, kroute_compare) + +RB_HEAD(kif_tree, kif_node) kit; +RB_PROTOTYPE(kif_tree, kif_node, entry, kif_compare) +RB_GENERATE(kif_tree, kif_node, entry, kif_compare) + +struct kroute kr_all_routers; +int flag_implicit_null = 0; +int flag_all_routers = 0; + +int +kif_init(void) +{ + RB_INIT(&kit); + + if (fetchifs(0) == -1) + return (-1); + + return (0); +} + +int +kr_init(int fs) +{ + int opt = 0, rcvbuf, default_rcvbuf; + socklen_t optlen; + + kr_state.fib_sync = fs; + + if ((kr_state.fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) { + log_warn("kr_init: socket"); + return (-1); + } + + /* not interested in my own messages */ + if (setsockopt(kr_state.fd, SOL_SOCKET, SO_USELOOPBACK, + &opt, sizeof(opt)) == -1) + log_warn("kr_init: setsockopt"); /* not fatal */ + + /* grow receive buffer, don't wanna miss messages */ + optlen = sizeof(default_rcvbuf); + if (getsockopt(kr_state.fd, SOL_SOCKET, SO_RCVBUF, + &default_rcvbuf, &optlen) == -1) + log_warn("kr_init getsockopt SOL_SOCKET SO_RCVBUF"); + else + for (rcvbuf = MAX_RTSOCK_BUF; + rcvbuf > default_rcvbuf && + setsockopt(kr_state.fd, SOL_SOCKET, SO_RCVBUF, + &rcvbuf, sizeof(rcvbuf)) == -1 && errno == ENOBUFS; + rcvbuf /= 2) + ; /* nothing */ + + kr_state.pid = getpid(); + kr_state.rtseq = 1; + + RB_INIT(&krt); + + if (fetchtable() == -1) + return (-1); + + if (protect_lo() == -1) + return (-1); + + kr_all_routers.prefix.s_addr = inet_addr(AllRouters); + kr_all_routers.prefixlen = mask2prefixlen(INADDR_BROADCAST); + kr_all_routers.nexthop.s_addr = htonl(INADDR_LOOPBACK); + + kr_state.fib_sync = 1; /* force addition of multicast route */ + if (send_rtmsg(kr_state.fd, RTM_ADD, &kr_all_routers) != -1) + flag_all_routers = 1; + + kr_state.fib_sync = fs; /* now set correct sync mode */ + + event_set(&kr_state.ev, kr_state.fd, EV_READ | EV_PERSIST, + kr_dispatch_msg, NULL); + event_add(&kr_state.ev, NULL); + + return (0); +} + +int +kr_change_fib(struct kroute_node *kr, struct kroute *kroute, int krcount, + int action) +{ + int i; + struct kroute_node *kn, *nkn; + + if (action == RTM_ADD) { + /* + * First remove all stale multipath routes. + * This step must be skipped when the action is RTM_CHANGE + * because it is already a single path route that will be + * changed. + */ + for (kn = kr; kn != NULL; kn = nkn) { + for (i = 0; i < krcount; i++) { + if (kn->r.nexthop.s_addr == + kroute[i].nexthop.s_addr) + break; + } + nkn = kn->next; + if (i == krcount) + /* stale route */ + if (kr_delete_fib(kn) == -1) + log_warnx("kr_delete_fib failed"); + log_debug("kr_update_fib: before: %s%s", + inet_ntoa(kn->r.nexthop), + i == krcount ? " (deleted)" : ""); + } + } + + /* + * now add or change the route + */ + for (i = 0; i < krcount; i++) { + /* nexthop within 127/8 -> ignore silently */ + if ((kroute[i].nexthop.s_addr & htonl(IN_CLASSA_NET)) == + htonl(INADDR_LOOPBACK & IN_CLASSA_NET)) + continue; + if (action == RTM_ADD && kr) { + for (kn = kr; kn != NULL; kn = kn->next) { + if (kn->r.nexthop.s_addr == + kroute[i].nexthop.s_addr) + break; + } + + log_debug("kr_update_fib: after : %s%s", + inet_ntoa(kroute[i].nexthop), + kn == NULL ? " (added)" : ""); + + if (kn != NULL) + /* nexthop already present, skip it */ + continue; + } else + /* modify first entry */ + kn = kr; + + /* send update */ + if (send_rtlabelmsg(kr_state.fd, action, &kroute[i], AF_MPLS) + == -1) + return (-1); + if (kroute[i].nexthop.s_addr != NULL) { + if (send_rtlabelmsg(kr_state.fd, action, &kroute[i], + AF_INET) == -1) + return (-1); + } + + /* create new entry unless we are changing the first entry */ + if (action == RTM_ADD) + if ((kn = calloc(1, sizeof(*kn))) == NULL) + fatal(NULL); + + kn->r.prefix.s_addr = kroute[i].prefix.s_addr; + kn->r.prefixlen = kroute[i].prefixlen; + kn->r.local_label = kroute[i].local_label; + kn->r.remote_label = kroute[i].remote_label; + kn->r.nexthop.s_addr = kroute[i].nexthop.s_addr; + kn->r.flags = kroute[i].flags | F_LDPD_INSERTED; + kn->r.ext_tag = kroute[i].ext_tag; + rtlabel_unref(kn->r.rtlabel); /* for RTM_CHANGE */ + kn->r.rtlabel = kroute[i].rtlabel; + + if (action == RTM_ADD) + if (kroute_insert(kn) == -1) { + log_debug("kr_update_fib: cannot insert %s", + inet_ntoa(kn->r.nexthop)); + free(kn); + } + action = RTM_ADD; + } + return (0); +} + +int +kr_change(struct kroute *kroute, int krcount) +{ + struct kroute_node *kr; + int action = RTM_ADD; + + kroute->rtlabel = rtlabel_tag2id(kroute->ext_tag); + + kr = kroute_find(kroute->prefix.s_addr, kroute->prefixlen); + + if (kr != NULL) { + if (kr->r.flags & F_KERNEL) + action = RTM_CHANGE; + else if (kr->next == NULL) /* single path route */ + action = RTM_CHANGE; + } + + return (kr_change_fib(kr, kroute, krcount, action)); +} + +int +kr_delete_fib(struct kroute_node *kr) +{ + if (!(kr->r.flags & F_LDPD_INSERTED)) + return (0); + + if (kr->r.flags & F_KERNEL) { + /* remove F_LDPD_INSERTED flag, route still exists in kernel */ + kr->r.flags &= ~F_LDPD_INSERTED; + return (0); + } + + if (send_rtmsg(kr_state.fd, RTM_DELETE, &kr->r) == -1) + return (-1); + + if (kroute_remove(kr) == -1) + return (-1); + + return (0); +} + +int +kr_delete(struct kroute *kroute) +{ + struct kroute_node *kr, *nkr; + + if ((kr = kroute_find(kroute->prefix.s_addr, kroute->prefixlen)) == + NULL) + return (0); + + while (kr != NULL) { + nkr = kr->next; + if (kr_delete_fib(kr) == -1) + return (-1); + kr = nkr; + } + return (0); +} + +void +kr_shutdown(void) +{ + kr_lfib_decouple(); + + if (flag_all_routers) { + kr_state.fib_sync = 1; /* force removal of mulitcast route */ + (void)send_rtmsg(kr_state.fd, RTM_DELETE, &kr_all_routers); + } + + kroute_clear(); + kif_clear(); +} + +void +kr_lfib_couple(void) +{ + struct kroute_node *kr; + struct kroute_node *kn; + + if (kr_state.fib_sync == 1) /* already coupled */ + return; + + kr_state.fib_sync = 1; + + RB_FOREACH(kr, kroute_tree, &krt) + if (!(kr->r.flags & F_KERNEL)) + for (kn = kr; kn != NULL; kn = kn->next) { + send_rtmsg(kr_state.fd, RTM_ADD, &kn->r); + } + + log_info("kernel routing table coupled"); +} + +void +kr_lfib_decouple(void) +{ + struct kroute_node *kr; + struct kroute_node *kn; + + if (kr_state.fib_sync == 0) /* already decoupled */ + return; + + RB_FOREACH(kr, kroute_tree, &krt) { + if (kr->r.flags & F_LDPD_INSERTED) { + for (kn = kr; kn != NULL; kn = kn->next) { + send_rtlabelmsg(kr_state.fd, RTM_DELETE, + &kn->r, AF_MPLS); + + if (kr->r.nexthop.s_addr != NULL) { + send_rtlabelmsg(kr_state.fd, RTM_DELETE, + &kn->r, AF_INET); + } + } + } + } + + kr_state.fib_sync = 0; + + log_info("kernel routing table decoupled"); +} + +/* ARGSUSED */ +void +kr_dispatch_msg(int fd, short event, void *bula) +{ + dispatch_rtmsg(); +} + +void +kr_show_route(struct imsg *imsg) +{ + struct kroute_node *kr; + struct kroute_node *kn; + int flags; + struct in_addr addr; + + switch (imsg->hdr.type) { + case IMSG_CTL_KROUTE: + if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(flags)) { + log_warnx("kr_show_route: wrong imsg len"); + return; + } + memcpy(&flags, imsg->data, sizeof(flags)); + RB_FOREACH(kr, kroute_tree, &krt) + if (!flags || kr->r.flags & flags) { + kn = kr; + do { + main_imsg_compose_ldpe(IMSG_CTL_KROUTE, + imsg->hdr.pid, + &kn->r, sizeof(kn->r)); + } while ((kn = kn->next) != NULL); + } + break; + case IMSG_CTL_KROUTE_ADDR: + if (imsg->hdr.len != IMSG_HEADER_SIZE + + sizeof(struct in_addr)) { + log_warnx("kr_show_route: wrong imsg len"); + return; + } + memcpy(&addr, imsg->data, sizeof(addr)); + kr = NULL; + kr = kroute_match(addr.s_addr); + if (kr != NULL) + main_imsg_compose_ldpe(IMSG_CTL_KROUTE, imsg->hdr.pid, + &kr->r, sizeof(kr->r)); + break; + default: + log_debug("kr_show_route: error handling imsg"); + break; + } + + main_imsg_compose_ldpe(IMSG_CTL_END, imsg->hdr.pid, NULL, 0); +} + +void +kr_ifinfo(char *ifname, pid_t pid) +{ + struct kif_node *kif; + + RB_FOREACH(kif, kif_tree, &kit) + if (ifname == NULL || !strcmp(ifname, kif->k.ifname)) { + main_imsg_compose_ldpe(IMSG_CTL_IFINFO, + pid, &kif->k, sizeof(kif->k)); + } + + main_imsg_compose_ldpe(IMSG_CTL_END, pid, NULL, 0); +} + +void +kr_redist_remove(struct kroute_node *kh, struct kroute_node *kn) +{ + struct rroute rr; + + /* was the route redistributed? */ + if ((kn->r.flags & F_REDISTRIBUTED) == 0) + return; + + /* remove redistributed flag */ + kn->r.flags &= ~F_REDISTRIBUTED; + rr.kr = kn->r; + + /* probably inform the RDE (check if no other path is redistributed) */ + for (kn = kh; kn; kn = kn->next) + if (kn->r.flags & F_REDISTRIBUTED) + break; + + if (kn == NULL) + main_imsg_compose_lde(IMSG_NETWORK_DEL, 0, &rr, + sizeof(struct rroute)); +} + +int +kr_redist_eval(struct kroute *kr, struct rroute *rr) +{ + u_int32_t a, metric = 0; + + /* Only static routes are considered for redistribution. */ + if (!(kr->flags & F_KERNEL)) + goto dont_redistribute; + + /* Dynamic routes are not redistributable. */ + if (kr->flags & F_DYNAMIC) + goto dont_redistribute; + + /* interface is not up and running so don't announce */ + if (kr->flags & F_DOWN) + goto dont_redistribute; + + /* + * We consider the loopback net, multicast and experimental addresses + * as not redistributable. + */ + a = ntohl(kr->prefix.s_addr); + if (IN_MULTICAST(a) || IN_BADCLASS(a) || + (a >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) + goto dont_redistribute; + /* + * Consider networks with nexthop loopback as not redistributable. + */ + if (kr->nexthop.s_addr == htonl(INADDR_LOOPBACK)) + goto dont_redistribute; + + /* prefix should be redistributed */ + kr->flags |= F_REDISTRIBUTED; + /* + * only on of all multipath routes can be redistributed so + * redistribute the best one. + */ + if (rr->metric > metric) { + rr->kr = *kr; + rr->metric = metric; + } + return (1); + +dont_redistribute: + /* was the route redistributed? */ + if ((kr->flags & F_REDISTRIBUTED) == 0) + return (0); + + kr->flags &= ~F_REDISTRIBUTED; + return (1); +} + +void +kr_redistribute(struct kroute_node *kh) +{ + struct kroute_node *kn; + struct rroute rr; + int redistribute = 0; + + bzero(&rr, sizeof(rr)); + rr.metric = UINT_MAX; + for (kn = kh; kn; kn = kn->next) + if (kr_redist_eval(&kn->r, &rr)) + redistribute = 1; + + if (!redistribute) + return; + + if (rr.kr.flags & F_REDISTRIBUTED) { + main_imsg_compose_lde(IMSG_NETWORK_ADD, 0, &rr, + sizeof(struct rroute)); + } else { + rr.kr = kh->r; + main_imsg_compose_lde(IMSG_NETWORK_DEL, 0, &rr, + sizeof(struct rroute)); + } +} + +void +kr_reload(void) +{ + struct kroute_node *kr, *kn; + + RB_FOREACH(kr, kroute_tree, &krt) { + for (kn = kr; kn; kn = kn->next) { + /* + * if it is redistributed, redistribute again metric + * may have changed. + */ + if (kn->r.flags & F_REDISTRIBUTED) + break; + } + if (kn) { + /* + * kr_redistribute copes with removes and RDE with + * duplicates + */ + kr_redistribute(kr); + } + } +} + +/* rb-tree compare */ +int +kroute_compare(struct kroute_node *a, struct kroute_node *b) +{ + if (ntohl(a->r.prefix.s_addr) < ntohl(b->r.prefix.s_addr)) + return (-1); + if (ntohl(a->r.prefix.s_addr) > ntohl(b->r.prefix.s_addr)) + return (1); + if (a->r.prefixlen < b->r.prefixlen) + return (-1); + if (a->r.prefixlen > b->r.prefixlen) + return (1); + return (0); +} + +int +kif_compare(struct kif_node *a, struct kif_node *b) +{ + return (b->k.ifindex - a->k.ifindex); +} + +/* tree management */ +struct kroute_node * +kroute_find(in_addr_t prefix, u_int8_t prefixlen) +{ + struct kroute_node s; + + s.r.prefix.s_addr = prefix; + s.r.prefixlen = prefixlen; + + return (RB_FIND(kroute_tree, &krt, &s)); +} + +struct kroute_node * +kroute_matchgw(struct kroute_node *kr, struct in_addr nh) +{ + in_addr_t nexthop; + + nexthop = nh.s_addr; + + while (kr) { + if (kr->r.nexthop.s_addr == nexthop) + return (kr); + kr = kr->next; + } + + return (NULL); +} + + +int +kroute_insert(struct kroute_node *kr) +{ + struct kroute_node *krm; + + if ((krm = RB_INSERT(kroute_tree, &krt, kr)) != NULL) { + /* + * Multipath route, add at end of list and clone the + * ldpd inserted flag. + */ + kr->r.flags |= krm->r.flags & F_LDPD_INSERTED; + while (krm->next != NULL) + krm = krm->next; + krm->next = kr; + kr->next = NULL; /* to be sure */ + } else + krm = kr; + + if (!(kr->r.flags & F_KERNEL)) { + kr->r.flags &= ~F_DOWN; + return (0); + } + + if (kif_validate(kr->r.ifindex)) + kr->r.flags &= ~F_DOWN; + else + kr->r.flags |= F_DOWN; + + kr_redistribute(krm); + return (0); +} + +int +kroute_insert_label(struct kroute *kr) +{ + struct kroute_node *krn; + + krn = kroute_find(kr->prefix.s_addr, kr->prefixlen); + if (krn == NULL) { + log_debug("kroute_insert_label: prefix %s/%d not present", + inet_ntoa(kr->prefix), kr->prefixlen); + return (-1); + } + + krn->r.flags |= F_LDPD_INSERTED; + krn->r.local_label = kr->local_label; + krn->r.remote_label = kr->remote_label; + + send_rtlabelmsg(kr_state.fd, RTM_ADD, kr, AF_MPLS); + + if (kr->nexthop.s_addr != INADDR_ANY) + send_rtlabelmsg(kr_state.fd, RTM_ADD, kr, AF_INET); + + return (0); +} + +int +kroute_remove(struct kroute_node *kr) +{ + struct kroute_node *krm; + + if ((krm = RB_FIND(kroute_tree, &krt, kr)) == NULL) { + log_warnx("kroute_remove failed to find %s/%u", + inet_ntoa(kr->r.prefix), kr->r.prefixlen); + return (-1); + } + + if (krm == kr) { + /* head element */ + if (RB_REMOVE(kroute_tree, &krt, kr) == NULL) { + log_warnx("kroute_remove failed for %s/%u", + inet_ntoa(kr->r.prefix), kr->r.prefixlen); + return (-1); + } + if (kr->next != NULL) { + if (RB_INSERT(kroute_tree, &krt, kr->next) != NULL) { + log_warnx("kroute_remove failed to add %s/%u", + inet_ntoa(kr->r.prefix), kr->r.prefixlen); + return (-1); + } + } + } else { + /* somewhere in the list */ + while (krm->next != kr && krm->next != NULL) + krm = krm->next; + if (krm->next == NULL) { + log_warnx("kroute_remove multipath list corrupted " + "for %s/%u", inet_ntoa(kr->r.prefix), + kr->r.prefixlen); + return (-1); + } + krm->next = kr->next; + } + + kr_redist_remove(krm, kr); + rtlabel_unref(kr->r.rtlabel); + + free(kr); + return (0); +} + +void +kroute_clear(void) +{ + struct kroute_node *kr; + + while ((kr = RB_MIN(kroute_tree, &krt)) != NULL) + kroute_remove(kr); +} + +struct kif_node * +kif_find(u_short ifindex) +{ + struct kif_node s; + + bzero(&s, sizeof(s)); + s.k.ifindex = ifindex; + + return (RB_FIND(kif_tree, &kit, &s)); +} + +struct kif * +kif_findname(char *ifname, struct in_addr addr, struct kif_addr **kap) +{ + struct kif_node *kif; + struct kif_addr *ka; + + RB_FOREACH(kif, kif_tree, &kit) + if (!strcmp(ifname, kif->k.ifname)) { + ka = TAILQ_FIRST(&kif->addrs); + if (addr.s_addr != 0) { + TAILQ_FOREACH(ka, &kif->addrs, entry) { + if (addr.s_addr == ka->addr.s_addr) + break; + } + } + if (kap != NULL) + *kap = ka; + return (&kif->k); + } + + return (NULL); +} + +struct kif_node * +kif_insert(u_short ifindex) +{ + struct kif_node *kif; + + if ((kif = calloc(1, sizeof(struct kif_node))) == NULL) + return (NULL); + + kif->k.ifindex = ifindex; + TAILQ_INIT(&kif->addrs); + + if (RB_INSERT(kif_tree, &kit, kif) != NULL) + fatalx("kif_insert: RB_INSERT"); + + return (kif); +} + +int +kif_remove(struct kif_node *kif) +{ + struct kif_addr *ka; + + if (RB_REMOVE(kif_tree, &kit, kif) == NULL) { + log_warnx("RB_REMOVE(kif_tree, &kit, kif)"); + return (-1); + } + + while ((ka = TAILQ_FIRST(&kif->addrs)) != NULL) { + TAILQ_REMOVE(&kif->addrs, ka, entry); + free(ka); + } + free(kif); + return (0); +} + +void +kif_clear(void) +{ + struct kif_node *kif; + + while ((kif = RB_MIN(kif_tree, &kit)) != NULL) + kif_remove(kif); +} + +struct kif * +kif_update(u_short ifindex, int flags, struct if_data *ifd, + struct sockaddr_dl *sdl) +{ + struct kif_node *kif; + + if ((kif = kif_find(ifindex)) == NULL) + if ((kif = kif_insert(ifindex)) == NULL) + return (NULL); + + kif->k.flags = flags; + kif->k.link_state = ifd->ifi_link_state; + kif->k.media_type = ifd->ifi_type; + kif->k.baudrate = ifd->ifi_baudrate; + kif->k.mtu = ifd->ifi_mtu; + + if (sdl && sdl->sdl_family == AF_LINK) { + if (sdl->sdl_nlen >= sizeof(kif->k.ifname)) + memcpy(kif->k.ifname, sdl->sdl_data, + sizeof(kif->k.ifname) - 1); + else if (sdl->sdl_nlen > 0) + memcpy(kif->k.ifname, sdl->sdl_data, + sdl->sdl_nlen); + /* string already terminated via calloc() */ + } + + return (&kif->k); +} + +int +kif_validate(u_short ifindex) +{ + struct kif_node *kif; + + if ((kif = kif_find(ifindex)) == NULL) { + log_warnx("interface with index %u not found", ifindex); + return (1); + } + + return (kif->k.nh_reachable); +} + +struct kroute_node * +kroute_match(in_addr_t key) +{ + int i; + struct kroute_node *kr; + + /* we will never match the default route */ + for (i = 32; i > 0; i--) + if ((kr = kroute_find(key & prefixlen2mask(i), i)) != NULL) + return (kr); + + /* if we don't have a match yet, try to find a default route */ + if ((kr = kroute_find(0, 0)) != NULL) + return (kr); + + return (NULL); +} + +/* misc */ +int +protect_lo(void) +{ + struct kroute_node *kr; + + /* special protection for 127/8 */ + if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) { + log_warn("protect_lo"); + return (-1); + } + kr->r.prefix.s_addr = htonl(INADDR_LOOPBACK); + kr->r.prefixlen = 8; + kr->r.flags = F_KERNEL|F_CONNECTED; + + if (RB_INSERT(kroute_tree, &krt, kr) != NULL) + free(kr); /* kernel route already there, no problem */ + + return (0); +} + +u_int8_t +prefixlen_classful(in_addr_t ina) +{ + /* it hurt to write this. */ + + if (ina >= 0xf0000000U) /* class E */ + return (32); + else if (ina >= 0xe0000000U) /* class D */ + return (4); + else if (ina >= 0xc0000000U) /* class C */ + return (24); + else if (ina >= 0x80000000U) /* class B */ + return (16); + else /* class A */ + return (8); +} + +u_int8_t +mask2prefixlen(in_addr_t ina) +{ + if (ina == 0) + return (0); + else + return (33 - ffs(ntohl(ina))); +} + +in_addr_t +prefixlen2mask(u_int8_t prefixlen) +{ + if (prefixlen == 0) + return (0); + + return (htonl(0xffffffff << (32 - prefixlen))); +} + +#define ROUNDUP(a, size) \ + (((a) & ((size) - 1)) ? (1 + ((a) | ((size) - 1))) : (a)) + +void +get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) +{ + int i; + + for (i = 0; i < RTAX_MAX; i++) { + if (addrs & (1 << i)) { + rti_info[i] = sa; + sa = (struct sockaddr *)((char *)(sa) + + ROUNDUP(sa->sa_len, sizeof(long))); + } else + rti_info[i] = NULL; + } +} + +void +if_change(u_short ifindex, int flags, struct if_data *ifd) +{ + struct kroute_node *kr, *tkr; + struct kif *kif; + u_int8_t reachable; + + if ((kif = kif_update(ifindex, flags, ifd, NULL)) == NULL) { + log_warn("if_change: kif_update(%u)", ifindex); + return; + } + + reachable = (kif->flags & IFF_UP) && + (LINK_STATE_IS_UP(kif->link_state) || + (kif->link_state == LINK_STATE_UNKNOWN && + kif->media_type != IFT_CARP)); + + if (reachable == kif->nh_reachable) + return; /* nothing changed wrt nexthop validity */ + + kif->nh_reachable = reachable; + + /* notify ldpe about interface link state */ + main_imsg_compose_ldpe(IMSG_IFINFO, 0, kif, sizeof(struct kif)); + + /* update redistribute list */ + RB_FOREACH(kr, kroute_tree, &krt) { + for (tkr = kr; tkr != NULL; tkr = tkr->next) { + if (tkr->r.ifindex == ifindex) { + if (reachable) + tkr->r.flags &= ~F_DOWN; + else + tkr->r.flags |= F_DOWN; + + } + } + kr_redistribute(kr); + } +} + +void +if_newaddr(u_short ifindex, struct sockaddr_in *ifa, struct sockaddr_in *mask, + struct sockaddr_in *brd) +{ + struct kif_node *kif; + struct kif_addr *ka; + + if (ifa == NULL || ifa->sin_family != AF_INET) + return; + if ((kif = kif_find(ifindex)) == NULL) { + log_warnx("if_newaddr: corresponding if %i not found", ifindex); + return; + } + if ((ka = calloc(1, sizeof(struct kif_addr))) == NULL) + fatal("if_newaddr"); + ka->addr = ifa->sin_addr; + if (mask) + ka->mask = mask->sin_addr; + else + ka->mask.s_addr = INADDR_NONE; + if (brd) + ka->dstbrd = brd->sin_addr; + else + ka->dstbrd.s_addr = INADDR_NONE; + + TAILQ_INSERT_TAIL(&kif->addrs, ka, entry); +} + +void +if_announce(void *msg) +{ + struct if_announcemsghdr *ifan; + struct kif_node *kif; + + ifan = msg; + + switch (ifan->ifan_what) { + case IFAN_ARRIVAL: + kif = kif_insert(ifan->ifan_index); + strlcpy(kif->k.ifname, ifan->ifan_name, sizeof(kif->k.ifname)); + break; + case IFAN_DEPARTURE: + kif = kif_find(ifan->ifan_index); + kif_remove(kif); + break; + } +} + +/* rtsock */ +int +send_rtlabelmsg(int fd, int action, struct kroute *kroute, u_int32_t family) +{ + struct iovec iov[5]; + struct rt_msghdr hdr; + struct sockaddr_mpls label_in, label_out; + struct sockaddr_in dst, mask, nexthop; + u_int32_t hr_label; + int iovcnt = 0; + + if (kr_state.fib_sync == 0) + return (0); + + hr_label = ntohl(kroute->local_label) >> MPLS_LABEL_OFFSET; + if (hr_label == MPLS_LABEL_IMPLNULL) { + if (action == RTM_ADD && flag_implicit_null) + return (0); + + if (action == RTM_DELETE && !flag_implicit_null) + return (0); + } + + if (family == AF_INET && kroute->remote_label == 0) + return (0); + + if (family == AF_INET && action == RTM_ADD) + action = RTM_CHANGE; + + /* initialize header */ + bzero(&hdr, sizeof(hdr)); + hdr.rtm_version = RTM_VERSION; + hdr.rtm_type = action; + hdr.rtm_flags = RTF_UP; +/* if (action == RTM_CHANGE) + hdr.rtm_fmask = RTF_PROTO3|RTF_PROTO2|RTF_PROTO1| + RTF_REJECT|RTF_BLACKHOLE; +*/ + hdr.rtm_priority = 0; + hdr.rtm_seq = kr_state.rtseq++; /* overflow doesn't matter */ + hdr.rtm_msglen = sizeof(hdr); + /* adjust iovec */ + iov[iovcnt].iov_base = &hdr; + iov[iovcnt++].iov_len = sizeof(hdr); + + if (family == AF_MPLS) { + bzero(&label_in, sizeof(label_in)); + label_in.smpls_len = sizeof(label_in); + label_in.smpls_family = AF_MPLS; + label_in.smpls_label = kroute->local_label; + /* adjust header */ + hdr.rtm_addrs |= RTA_DST; + hdr.rtm_msglen += sizeof(label_in); + /* adjust iovec */ + iov[iovcnt].iov_base = &label_in; + iov[iovcnt++].iov_len = sizeof(label_in); + } else { + bzero(&dst, sizeof(dst)); + dst.sin_len = sizeof(dst); + dst.sin_family = AF_INET; + dst.sin_addr.s_addr = kroute->prefix.s_addr; + /* adjust header */ + hdr.rtm_addrs |= RTA_DST; + hdr.rtm_msglen += sizeof(dst); + /* adjust iovec */ + iov[iovcnt].iov_base = &dst; + iov[iovcnt++].iov_len = sizeof(dst); + } + + bzero(&nexthop, sizeof(nexthop)); + nexthop.sin_len = sizeof(nexthop); + nexthop.sin_family = AF_INET; + nexthop.sin_addr.s_addr = kroute->nexthop.s_addr; + /* adjust header */ + hdr.rtm_flags |= RTF_GATEWAY; + hdr.rtm_addrs |= RTA_GATEWAY; + hdr.rtm_msglen += sizeof(nexthop); + /* adjust iovec */ + iov[iovcnt].iov_base = &nexthop; + iov[iovcnt++].iov_len = sizeof(nexthop); + + if (family == AF_INET) { + bzero(&mask, sizeof(mask)); + mask.sin_len = sizeof(mask); + mask.sin_family = AF_INET; + mask.sin_addr.s_addr = prefixlen2mask(kroute->prefixlen); + /* adjust header */ + hdr.rtm_addrs |= RTA_NETMASK; + hdr.rtm_msglen += sizeof(mask); + /* adjust iovec */ + iov[iovcnt].iov_base = &mask; + iov[iovcnt++].iov_len = sizeof(mask); + } + + if (kroute->remote_label != 0) { + bzero(&label_out, sizeof(label_out)); + label_out.smpls_len = sizeof(label_out); + label_out.smpls_family = AF_MPLS; + label_out.smpls_label = kroute->remote_label; + /* adjust header */ + hdr.rtm_addrs |= RTA_SRC; + hdr.rtm_msglen += sizeof(label_out); + /* adjust iovec */ + iov[iovcnt].iov_base = &label_out; + iov[iovcnt++].iov_len = sizeof(label_out); + + if (family == AF_MPLS) + hdr.rtm_mpls = MPLS_OP_SWAP; + else + hdr.rtm_mpls = MPLS_OP_PUSH; + } + +retry: + if (writev(fd, iov, iovcnt) == -1) { + switch (errno) { + case ESRCH: + if (hdr.rtm_type == RTM_CHANGE) { + hdr.rtm_type = RTM_ADD; + goto retry; + } else if (hdr.rtm_type == RTM_DELETE) { + log_info("route %s/%u vanished before delete", + inet_ntoa(kroute->prefix), + kroute->prefixlen); + return (0); + } else { + log_warnx("send_rtlabelmsg: action %u, " + "prefix %s/%u: %s", hdr.rtm_type, + inet_ntoa(kroute->prefix), + kroute->prefixlen, strerror(errno)); + return (0); + } + break; + default: + log_warnx("send_rtlabelmsg: action %u, " + "prefix %s/%u: %s", hdr.rtm_type, + inet_ntoa(kroute->prefix), + kroute->prefixlen, strerror(errno)); + return (0); + } + } + + if (hr_label == MPLS_LABEL_IMPLNULL) { + if (action == RTM_ADD) + flag_implicit_null = 1; + + if (action == RTM_DELETE) + flag_implicit_null = 0; + } + + return (0); +} + +int +send_rtmsg(int fd, int action, struct kroute *kroute) +{ + struct iovec iov[5]; + struct rt_msghdr hdr; + struct sockaddr_in prefix; + struct sockaddr_in nexthop; + struct sockaddr_in mask; + struct sockaddr_rtlabel sa_rl; + int iovcnt = 0; + const char *label; + + /* initialize header */ + bzero(&hdr, sizeof(hdr)); + hdr.rtm_version = RTM_VERSION; + hdr.rtm_type = action; + hdr.rtm_flags = RTF_MPATH; + hdr.rtm_priority = RTP_STATIC; + if (action == RTM_CHANGE) /* force PROTO2 reset the other flags */ + hdr.rtm_fmask = RTF_PROTO2|RTF_PROTO1|RTF_REJECT|RTF_BLACKHOLE; + hdr.rtm_seq = kr_state.rtseq++; /* overflow doesn't matter */ + hdr.rtm_msglen = sizeof(hdr); + /* adjust iovec */ + iov[iovcnt].iov_base = &hdr; + iov[iovcnt++].iov_len = sizeof(hdr); + + bzero(&prefix, sizeof(prefix)); + prefix.sin_len = sizeof(prefix); + prefix.sin_family = AF_INET; + prefix.sin_addr.s_addr = kroute->prefix.s_addr; + /* adjust header */ + hdr.rtm_addrs |= RTA_DST; + hdr.rtm_msglen += sizeof(prefix); + /* adjust iovec */ + iov[iovcnt].iov_base = &prefix; + iov[iovcnt++].iov_len = sizeof(prefix); + + if (kroute->nexthop.s_addr != 0) { + bzero(&nexthop, sizeof(nexthop)); + nexthop.sin_len = sizeof(nexthop); + nexthop.sin_family = AF_INET; + nexthop.sin_addr.s_addr = kroute->nexthop.s_addr; + /* adjust header */ + hdr.rtm_flags |= RTF_GATEWAY; + hdr.rtm_addrs |= RTA_GATEWAY; + hdr.rtm_msglen += sizeof(nexthop); + /* adjust iovec */ + iov[iovcnt].iov_base = &nexthop; + iov[iovcnt++].iov_len = sizeof(nexthop); + } + + bzero(&mask, sizeof(mask)); + mask.sin_len = sizeof(mask); + mask.sin_family = AF_INET; + mask.sin_addr.s_addr = prefixlen2mask(kroute->prefixlen); + /* adjust header */ + hdr.rtm_addrs |= RTA_NETMASK; + hdr.rtm_msglen += sizeof(mask); + /* adjust iovec */ + iov[iovcnt].iov_base = &mask; + iov[iovcnt++].iov_len = sizeof(mask); + + if (kroute->rtlabel != 0) { + sa_rl.sr_len = sizeof(sa_rl); + sa_rl.sr_family = AF_UNSPEC; + label = rtlabel_id2name(kroute->rtlabel); + if (strlcpy(sa_rl.sr_label, label, + sizeof(sa_rl.sr_label)) >= sizeof(sa_rl.sr_label)) { + log_warnx("send_rtmsg: invalid rtlabel"); + return (-1); + } + /* adjust header */ + hdr.rtm_addrs |= RTA_LABEL; + hdr.rtm_msglen += sizeof(sa_rl); + /* adjust iovec */ + iov[iovcnt].iov_base = &sa_rl; + iov[iovcnt++].iov_len = sizeof(sa_rl); + } + + +retry: + if (writev(fd, iov, iovcnt) == -1) { + switch (errno) { + case ESRCH: + if (hdr.rtm_type == RTM_CHANGE) { + hdr.rtm_type = RTM_ADD; + goto retry; + } else if (hdr.rtm_type == RTM_DELETE) { + log_info("route %s/%u vanished before delete", + inet_ntoa(kroute->prefix), + kroute->prefixlen); + return (0); + } else { + log_warnx("send_rtmsg: action %u, " + "prefix %s/%u: %s", hdr.rtm_type, + inet_ntoa(kroute->prefix), + kroute->prefixlen, strerror(errno)); + return (0); + } + break; + default: + log_warnx("send_rtmsg: action %u, prefix %s/%u: %s", + hdr.rtm_type, inet_ntoa(kroute->prefix), + kroute->prefixlen, strerror(errno)); + return (0); + } + } + + return (0); +} + +int +fetchtable(void) +{ + size_t len; + int mib[7]; + char *buf, *next, *lim; + struct rt_msghdr *rtm; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + struct sockaddr_in *sa_in; + struct sockaddr_rtlabel *label; + struct sockaddr_mpls *sa_mpls; + struct kroute_node *kr; + + mib[0] = CTL_NET; + mib[1] = AF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET; + mib[4] = NET_RT_DUMP; + mib[5] = 0; + mib[6] = 0; /* rtableid */ + + if (sysctl(mib, 7, NULL, &len, NULL, 0) == -1) { + log_warn("sysctl"); + return (-1); + } + if ((buf = malloc(len)) == NULL) { + log_warn("fetchtable"); + return (-1); + } + if (sysctl(mib, 7, buf, &len, NULL, 0) == -1) { + log_warn("sysctl"); + free(buf); + return (-1); + } + + lim = buf + len; + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + if (rtm->rtm_version != RTM_VERSION) + continue; + sa = (struct sockaddr *)(rtm + 1); + get_rtaddrs(rtm->rtm_addrs, sa, rti_info); + + if ((sa = rti_info[RTAX_DST]) == NULL) + continue; + + if (rtm->rtm_flags & RTF_LLINFO) /* arp cache */ + continue; + + if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) { + log_warn("fetchtable"); + free(buf); + return (-1); + } + + kr->r.flags = F_KERNEL; + + switch (sa->sa_family) { + case AF_INET: + kr->r.prefix.s_addr = + ((struct sockaddr_in *)sa)->sin_addr.s_addr; + sa_in = (struct sockaddr_in *)rti_info[RTAX_NETMASK]; + if (rtm->rtm_flags & RTF_STATIC) + kr->r.flags |= F_STATIC; + if (rtm->rtm_flags & RTF_DYNAMIC) + kr->r.flags |= F_DYNAMIC; + if (rtm->rtm_flags & RTF_PROTO1) + kr->r.flags |= F_BGPD_INSERTED; + if (sa_in != NULL) { + if (sa_in->sin_len == 0) + break; + kr->r.prefixlen = + mask2prefixlen(sa_in->sin_addr.s_addr); + } else if (rtm->rtm_flags & RTF_HOST) + kr->r.prefixlen = 32; + else + kr->r.prefixlen = + prefixlen_classful(kr->r.prefix.s_addr); + break; + default: + free(kr); + continue; + } + + kr->r.ifindex = rtm->rtm_index; + if ((sa = rti_info[RTAX_GATEWAY]) != NULL) + switch (sa->sa_family) { + case AF_INET: + kr->r.nexthop.s_addr = + ((struct sockaddr_in *)sa)->sin_addr.s_addr; + break; + case AF_LINK: + kr->r.flags |= F_CONNECTED; + break; + } + + if ((sa_mpls = (struct sockaddr_mpls *)rti_info[RTAX_SRC]) + != NULL) + kr->r.local_label = sa_mpls->smpls_label; + + if (rtm->rtm_flags & RTF_PROTO2) { + send_rtmsg(kr_state.fd, RTM_DELETE, &kr->r); + free(kr); + } else { + if ((label = (struct sockaddr_rtlabel *) + rti_info[RTAX_LABEL]) != NULL) { + kr->r.rtlabel = + rtlabel_name2id(label->sr_label); + kr->r.ext_tag = + rtlabel_id2tag(kr->r.rtlabel); + } + kroute_insert(kr); + } + + } + free(buf); + return (0); +} + +int +fetchifs(u_short ifindex) +{ + size_t len; + int mib[6]; + char *buf, *next, *lim; + struct rt_msghdr *rtm; + struct if_msghdr ifm; + struct ifa_msghdr *ifam; + struct kif *kif = NULL; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + + mib[0] = CTL_NET; + mib[1] = AF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET; + mib[4] = NET_RT_IFLIST; + mib[5] = ifindex; + + if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) { + log_warn("sysctl"); + return (-1); + } + if ((buf = malloc(len)) == NULL) { + log_warn("fetchif"); + return (-1); + } + if (sysctl(mib, 6, buf, &len, NULL, 0) == -1) { + log_warn("sysctl"); + free(buf); + return (-1); + } + + lim = buf + len; + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + if (rtm->rtm_version != RTM_VERSION) + continue; + switch (rtm->rtm_type) { + case RTM_IFINFO: + bcopy(rtm, &ifm, sizeof ifm); + sa = (struct sockaddr *)(next + rtm->rtm_hdrlen); + get_rtaddrs(ifm.ifm_addrs, sa, rti_info); + + if ((kif = kif_update(ifm.ifm_index, + ifm.ifm_flags, &ifm.ifm_data, + (struct sockaddr_dl *)rti_info[RTAX_IFP])) == NULL) + fatal("fetchifs"); + + kif->nh_reachable = (kif->flags & IFF_UP) && + (LINK_STATE_IS_UP(ifm.ifm_data.ifi_link_state) || + (ifm.ifm_data.ifi_link_state == + LINK_STATE_UNKNOWN && + ifm.ifm_data.ifi_type != IFT_CARP)); + break; + case RTM_NEWADDR: + ifam = (struct ifa_msghdr *)rtm; + if ((ifam->ifam_addrs & (RTA_NETMASK | RTA_IFA | + RTA_BRD)) == 0) + break; + sa = (struct sockaddr *)(ifam + 1); + get_rtaddrs(ifam->ifam_addrs, sa, rti_info); + + if_newaddr(ifam->ifam_index, + (struct sockaddr_in *)rti_info[RTAX_IFA], + (struct sockaddr_in *)rti_info[RTAX_NETMASK], + (struct sockaddr_in *)rti_info[RTAX_BRD]); + break; + } + } + free(buf); + return (0); +} + +int +dispatch_rtmsg(void) +{ + char buf[RT_BUF_SIZE]; + ssize_t n; + char *next, *lim; + struct rt_msghdr *rtm; + struct if_msghdr ifm; + struct ifa_msghdr *ifam; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + struct sockaddr_in *sa_in; + struct sockaddr_rtlabel *label; + struct kroute_node *kr, *okr; + struct in_addr prefix, nexthop; + u_int8_t prefixlen; + int flags, mpath; + u_short ifindex = 0; + + if ((n = read(kr_state.fd, &buf, sizeof(buf))) == -1) { + log_warn("dispatch_rtmsg: read error"); + return (-1); + } + + if (n == 0) { + log_warnx("routing socket closed"); + return (-1); + } + + lim = buf + n; + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + if (rtm->rtm_version != RTM_VERSION) + continue; + + prefix.s_addr = 0; + prefixlen = 0; + flags = F_KERNEL; + nexthop.s_addr = 0; + mpath = 0; + + if (rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_CHANGE || + rtm->rtm_type == RTM_DELETE) { + sa = (struct sockaddr *)(rtm + 1); + get_rtaddrs(rtm->rtm_addrs, sa, rti_info); + + if (rtm->rtm_tableid != 0) + continue; + + if (rtm->rtm_pid == kr_state.pid) /* caused by us */ + continue; + + if (rtm->rtm_errno) /* failed attempts... */ + continue; + + if (rtm->rtm_flags & RTF_LLINFO) /* arp cache */ + continue; + +#ifdef RTF_MPATH + if (rtm->rtm_flags & RTF_MPATH) + mpath = 1; +#endif + switch (sa->sa_family) { + case AF_INET: + prefix.s_addr = + ((struct sockaddr_in *)sa)->sin_addr.s_addr; + sa_in = (struct sockaddr_in *) + rti_info[RTAX_NETMASK]; + if (sa_in != NULL) { + if (sa_in->sin_len != 0) + prefixlen = mask2prefixlen( + sa_in->sin_addr.s_addr); + } else if (rtm->rtm_flags & RTF_HOST) + prefixlen = 32; + else + prefixlen = + prefixlen_classful(prefix.s_addr); + if (rtm->rtm_flags & RTF_STATIC) + flags |= F_STATIC; + if (rtm->rtm_flags & RTF_DYNAMIC) + flags |= F_DYNAMIC; + if (rtm->rtm_flags & RTF_PROTO1) + flags |= F_BGPD_INSERTED; + break; + default: + continue; + } + + ifindex = rtm->rtm_index; + if ((sa = rti_info[RTAX_GATEWAY]) != NULL) { + switch (sa->sa_family) { + case AF_INET: + nexthop.s_addr = ((struct + sockaddr_in *)sa)->sin_addr.s_addr; + break; + case AF_LINK: + flags |= F_CONNECTED; + break; + } + } + } + + switch (rtm->rtm_type) { + case RTM_ADD: + case RTM_CHANGE: + if (nexthop.s_addr == 0 && !(flags & F_CONNECTED)) { + log_warnx("dispatch_rtmsg no nexthop for %s/%u", + inet_ntoa(prefix), prefixlen); + continue; + } + + if ((okr = kroute_find(prefix.s_addr, prefixlen)) != + NULL) { + /* just add new multipath routes */ + if (mpath && rtm->rtm_type == RTM_ADD) + goto add; + /* get the correct route */ + kr = okr; + if (mpath && (kr = kroute_matchgw(okr, + nexthop)) == NULL) { + log_warnx("dispatch_rtmsg mpath route" + " not found"); + /* add routes we missed out earlier */ + goto add; + } + + /* + * ldp route overridden by kernel. Preference + * of the route is not checked because this is + * forced -- most probably by a user. + */ + if (kr->r.flags & F_LDPD_INSERTED) + flags |= F_LDPD_INSERTED; + if (kr->r.flags & F_REDISTRIBUTED) + flags |= F_REDISTRIBUTED; + kr->r.nexthop.s_addr = nexthop.s_addr; + kr->r.flags = flags; + kr->r.ifindex = ifindex; + + rtlabel_unref(kr->r.rtlabel); + kr->r.rtlabel = 0; + kr->r.ext_tag = 0; + if ((label = (struct sockaddr_rtlabel *) + rti_info[RTAX_LABEL]) != NULL) { + kr->r.rtlabel = + rtlabel_name2id(label->sr_label); + kr->r.ext_tag = + rtlabel_id2tag(kr->r.rtlabel); + } + + if (kif_validate(kr->r.ifindex)) + kr->r.flags &= ~F_DOWN; + else + kr->r.flags |= F_DOWN; + + /* just readd, the RDE will care */ + kr_redistribute(okr); + } else { +add: + if ((kr = calloc(1, + sizeof(struct kroute_node))) == NULL) { + log_warn("dispatch_rtmsg"); + return (-1); + } + kr->r.prefix.s_addr = prefix.s_addr; + kr->r.prefixlen = prefixlen; + kr->r.nexthop.s_addr = nexthop.s_addr; + kr->r.flags = flags; + kr->r.ifindex = ifindex; + + if ((label = (struct sockaddr_rtlabel *) + rti_info[RTAX_LABEL]) != NULL) { + kr->r.rtlabel = + rtlabel_name2id(label->sr_label); + kr->r.ext_tag = + rtlabel_id2tag(kr->r.rtlabel); + } + + kroute_insert(kr); + } + break; + case RTM_DELETE: + if ((kr = kroute_find(prefix.s_addr, prefixlen)) == + NULL) + continue; + if (!(kr->r.flags & F_KERNEL)) + continue; + /* get the correct route */ + okr = kr; + if (mpath && + (kr = kroute_matchgw(kr, nexthop)) == NULL) { + log_warnx("dispatch_rtmsg mpath route" + " not found"); + return (-1); + } + /* + * last route is getting removed request the + * ldp route from the RDE to insert instead + */ + if (okr == kr && kr->next == NULL && + kr->r.flags & F_LDPD_INSERTED) + main_imsg_compose_lde(IMSG_KROUTE_GET, 0, + &kr->r, sizeof(struct kroute)); + if (kroute_remove(kr) == -1) + return (-1); + break; + case RTM_IFINFO: + memcpy(&ifm, next, sizeof(ifm)); + if_change(ifm.ifm_index, ifm.ifm_flags, + &ifm.ifm_data); + break; + case RTM_NEWADDR: + ifam = (struct ifa_msghdr *)rtm; + if ((ifam->ifam_addrs & (RTA_NETMASK | RTA_IFA | + RTA_BRD)) == 0) + break; + sa = (struct sockaddr *)(ifam + 1); + get_rtaddrs(ifam->ifam_addrs, sa, rti_info); + + if_newaddr(ifam->ifam_index, + (struct sockaddr_in *)rti_info[RTAX_IFA], + (struct sockaddr_in *)rti_info[RTAX_NETMASK], + (struct sockaddr_in *)rti_info[RTAX_BRD]); + break; + case RTM_IFANNOUNCE: + if_announce(next); + break; + default: + /* ignore for now */ + break; + } + } + return (0); +} diff --git a/usr.sbin/ldpd/labelmapping.c b/usr.sbin/ldpd/labelmapping.c new file mode 100644 index 00000000000..aac097161b7 --- /dev/null +++ b/usr.sbin/ldpd/labelmapping.c @@ -0,0 +1,579 @@ +/* $OpenBSD: labelmapping.c,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <net/if_dl.h> +#include <unistd.h> + +#include <errno.h> +#include <event.h> +#include <stdlib.h> +#include <string.h> + +#include "ldpd.h" +#include "ldp.h" +#include "log.h" +#include "ldpe.h" + +void gen_fec_tlv(struct buf *, u_int32_t, u_int8_t); +void gen_label_tlv(struct buf *, u_int32_t); + +u_int32_t tlv_decode_label(struct label_tlv *); +u_int32_t decode_fec_elm(char *); +u_int8_t decode_fec_len_elm(char *); +int validate_fec_elm(char *); + +/* Label Mapping Message */ +int +send_labelmapping(struct nbr *nbr) +{ + struct buf *buf; + struct mapping_entry *me; + struct ldp_hdr *ldp_hdr; + u_int16_t tlv_size, size; + + if (nbr->iface->passive) + return (0); + + log_debug("send_labelmapping: neighbor ID %s", inet_ntoa(nbr->id)); + + if ((buf = buf_open(LDP_MAX_LEN)) == NULL) + fatal("send_labelmapping"); + + /* real size will be set up later */ + gen_ldp_hdr(buf, nbr->iface, 0); + + size = LDP_HDR_SIZE - TLV_HDR_LEN; + + TAILQ_FOREACH(me, &nbr->mapping_list, entry) { + tlv_size = BASIC_LABEL_MAP_LEN + me->prefixlen/8; + size += tlv_size; + + gen_msg_tlv(buf, MSG_TYPE_LABELMAPPING, tlv_size); + gen_fec_tlv(buf, me->prefix, me->prefixlen); + gen_label_tlv(buf, me->label); + } + + /* XXX: should we remove them first? */ + nbr_mapping_list_clr(nbr, &nbr->mapping_list); + + ldp_hdr = buf_seek(buf, 0, sizeof(struct ldp_hdr)); + ldp_hdr->length = htons(size); + + bufferevent_write(nbr->bev, buf->buf, buf->wpos); + buf_free(buf); + + return (0); +} + +int +recv_labelmapping(struct nbr *nbr, char *buf, u_int16_t len) +{ + struct ldp_msg *lm; + struct fec_tlv *ft; + struct label_tlv *lt; + struct map map; + u_int32_t messageid; + int feclen; + + log_debug("recv_labelmapping: neighbor ID %s", inet_ntoa(nbr->id)); + + if (nbr->state != NBR_STA_OPER) + return (-1); + + bzero(&map, sizeof(map)); + lm = (struct ldp_msg *)buf; + + if ((len - TLV_HDR_LEN) < ntohs(lm->length)) { + /* XXX: send notification */ + return (-1); + } + + messageid = lm->msgid; + + buf += sizeof(struct ldp_msg); + len -= sizeof(struct ldp_msg); + + ft = (struct fec_tlv *)buf; + lt = (struct label_tlv *)(buf + TLV_HDR_LEN + ntohs(ft->length)); + + if (len < (sizeof(*ft) + LABEL_TLV_LEN)) { + /* XXX: send notification */ + return (-1); + } + + feclen = ntohs(ft->length); + if (len - TLV_HDR_LEN < feclen) { + /* XXX: send notification */ + return (-1); + } + + map.label = tlv_decode_label(lt); + if (map.label == NO_LABEL) { + log_debug("recv_labelmapping: packet malformed from " + "neighbor ID %s", inet_ntoa(nbr->id)); + /* XXX: send notification */ + return (-1); + } + + buf += sizeof(struct fec_tlv); + len -= sizeof(struct fec_tlv); + + while (feclen >= FEC_ELM_MIN_LEN) { + if (validate_fec_elm(buf) < 0) { + /* XXX: send notification */ + return (-1); + } + + map.prefix = decode_fec_elm(buf); + map.prefixlen = decode_fec_len_elm(buf); + map.prefix &= prefixlen2mask(map.prefixlen); + + ldpe_imsg_compose_lde(IMSG_LABEL_MAPPING, nbr->peerid, 0, &map, + sizeof(map)); + + buf += FEC_ELM_MIN_LEN + map.prefixlen/8; + feclen -= (FEC_ELM_MIN_LEN + map.prefixlen/8); + } + + nbr_fsm(nbr, NBR_EVT_PDU_RCVD); + + return (ntohs(lm->length)); +} + +/* Label Request Message */ +int +send_labelrequest(struct nbr *nbr) +{ + struct buf *buf; + struct mapping_entry *me; + struct ldp_hdr *ldp_hdr; + u_int16_t tlv_size, size; + + if (nbr->iface->passive) + return (0); + + log_debug("send_labelrequest: neighbor ID %s", inet_ntoa(nbr->id)); + + if ((buf = buf_open(LDP_MAX_LEN)) == NULL) + fatal("send_labelrequest"); + + /* real size will be set up later */ + gen_ldp_hdr(buf, nbr->iface, 0); + + size = LDP_HDR_SIZE - TLV_HDR_LEN; + + TAILQ_FOREACH(me, &nbr->request_list, entry) { + tlv_size = me->prefixlen/8; + size += tlv_size; + + gen_msg_tlv(buf, MSG_TYPE_LABELREQUEST, tlv_size); + gen_fec_tlv(buf, me->prefix, me->prefixlen); + } + + /* XXX: should we remove them first? */ + nbr_mapping_list_clr(nbr, &nbr->request_list); + + ldp_hdr = buf_seek(buf, 0, sizeof(struct ldp_hdr)); + ldp_hdr->length = htons(size); + + bufferevent_write(nbr->bev, buf->buf, buf->wpos); + buf_free(buf); + + return (0); +} + +int +recv_labelrequest(struct nbr *nbr, char *buf, u_int16_t len) +{ + struct ldp_msg *lr; + struct fec_tlv *ft; + struct map map; + u_int32_t messageid; + int feclen; + + log_debug("recv_labelrequest: neighbor ID %s", inet_ntoa(nbr->id)); + + if (nbr->state != NBR_STA_OPER) + return (-1); + + bzero(&map, sizeof(map)); + lr = (struct ldp_msg *)buf; + + if ((len - TLV_HDR_LEN) < ntohs(lr->length)) { + /* XXX: send notification */ + return (-1); + } + + messageid = lr->msgid; + + buf += sizeof(struct ldp_msg); + len -= sizeof(struct ldp_msg); + + ft = (struct fec_tlv *)buf; + + if (len < sizeof(*ft) || + (len - TLV_HDR_LEN) < ntohs(ft->length)) { + /* XXX: send notification */ + return (-1); + } + + feclen = ntohs(ft->length); + + buf += sizeof(struct fec_tlv); + len -= sizeof(struct fec_tlv); + + while (feclen >= FEC_ELM_MIN_LEN) { + if (validate_fec_elm(buf) < 0) { + /* XXX: send notification */ + return (-1); + } + + map.prefix = decode_fec_elm(buf); + map.prefixlen = decode_fec_len_elm(buf); + map.prefix &= prefixlen2mask(map.prefixlen); + map.messageid = messageid; + + ldpe_imsg_compose_lde(IMSG_LABEL_REQUEST, nbr->peerid, 0, &map, + sizeof(map)); + + buf += FEC_ELM_MIN_LEN + map.prefixlen/8; + feclen -= (FEC_ELM_MIN_LEN + map.prefixlen/8); + } + + nbr_fsm(nbr, NBR_EVT_PDU_RCVD); + + return (0); +} + +/* Label Withdraw Message */ +int +send_labelwithdraw(struct nbr *nbr) +{ + struct buf *buf; + struct mapping_entry *me; + struct ldp_hdr *ldp_hdr; + u_int16_t tlv_size, size; + + if (nbr->iface->passive) + return (0); + + log_debug("send_labelwithdraw: neighbor ID %s", inet_ntoa(nbr->id)); + + if ((buf = buf_open(LDP_MAX_LEN)) == NULL) + fatal("send_labelwithdraw"); + + /* real size will be set up later */ + gen_ldp_hdr(buf, nbr->iface, 0); + + size = LDP_HDR_SIZE - TLV_HDR_LEN; + + TAILQ_FOREACH(me, &nbr->withdraw_list, entry) { + if (me->label == NO_LABEL) + tlv_size = me->prefixlen/8; + else + tlv_size = BASIC_LABEL_MAP_LEN + me->prefixlen/8; + + size += tlv_size; + + gen_msg_tlv(buf, MSG_TYPE_LABELWITHDRAW, tlv_size); + gen_fec_tlv(buf, me->prefix, me->prefixlen); + + if (me->label != NO_LABEL) + gen_label_tlv(buf, me->label); + } + + /* XXX: should we remove them first? */ + nbr_mapping_list_clr(nbr, &nbr->withdraw_list); + + ldp_hdr = buf_seek(buf, 0, sizeof(struct ldp_hdr)); + ldp_hdr->length = htons(size); + + bufferevent_write(nbr->bev, buf->buf, buf->wpos); + + buf_free(buf); + + return (0); +} + +int +recv_labelwithdraw(struct nbr *nbr, char *buf, u_int16_t len) +{ + struct ldp_msg *lw; + u_int32_t messageid; + + log_debug("recv_labelwithdraw: neighbor ID %s", inet_ntoa(nbr->id)); + + if (nbr->state != NBR_STA_OPER) + return (-1); + + lw = (struct ldp_msg *)buf; + + if ((len - TLV_HDR_LEN) < ntohs(lw->length)) { + /* XXX: send notification */ + return (-1); + } + + messageid = lw->msgid; + + buf += sizeof(struct ldp_msg); + len -= sizeof(struct ldp_msg); + + nbr_fsm(nbr, NBR_EVT_PDU_RCVD); + + return (0); +} + +/* Label Release Message */ +int +send_labelrelease(struct nbr *nbr) +{ + struct buf *buf; + struct mapping_entry *me; + struct ldp_hdr *ldp_hdr; + u_int16_t tlv_size, size; + + if (nbr->iface->passive) + return (0); + + log_debug("send_labelrelease: neighbor ID %s", inet_ntoa(nbr->id)); + + if ((buf = buf_open(LDP_MAX_LEN)) == NULL) + fatal("send_labelrelease"); + + /* real size will be set up later */ + gen_ldp_hdr(buf, nbr->iface, 0); + + size = LDP_HDR_SIZE - TLV_HDR_LEN; + + TAILQ_FOREACH(me, &nbr->release_list, entry) { + if (me->label == NO_LABEL) + tlv_size = me->prefixlen/8; + else + tlv_size = BASIC_LABEL_MAP_LEN + me->prefixlen/8; + + size += tlv_size; + + gen_msg_tlv(buf, MSG_TYPE_LABELRELEASE, tlv_size); + gen_fec_tlv(buf, me->prefix, me->prefixlen); + + if (me->label != NO_LABEL) + gen_label_tlv(buf, me->label); + } + + /* XXX: should we remove them first? */ + nbr_mapping_list_clr(nbr, &nbr->release_list); + + ldp_hdr = buf_seek(buf, 0, sizeof(struct ldp_hdr)); + ldp_hdr->length = htons(size); + + bufferevent_write(nbr->bev, buf->buf, buf->wpos); + buf_free(buf); + + return (0); +} + +int +recv_labelrelease(struct nbr *nbr, char *buf, u_int16_t len) +{ + struct ldp_msg *lr; + u_int32_t messageid; + + log_debug("recv_labelrelease: neighbor ID %s", inet_ntoa(nbr->id)); + + if (nbr->state != NBR_STA_OPER) + return (-1); + + lr = (struct ldp_msg *)buf; + + if ((len - TLV_HDR_LEN) < ntohs(lr->length)) { + /* XXX: send notification */ + return (-1); + } + + messageid = lr->msgid; + + buf += sizeof(struct ldp_msg); + len -= sizeof(struct ldp_msg); + + nbr_fsm(nbr, NBR_EVT_PDU_RCVD); + + return (0); +} + +/* Label Abort Req Message */ +int +send_labelabortreq(struct nbr *nbr) +{ + struct buf *buf; + u_int16_t size; + + if (nbr->iface->passive) + return (0); + + log_debug("send_labelabortreq: neighbor ID %s", inet_ntoa(nbr->id)); + + if ((buf = buf_open(LDP_MAX_LEN)) == NULL) + fatal("send_labelabortreq"); + + size = LDP_HDR_SIZE + sizeof(struct ldp_msg); + + gen_ldp_hdr(buf, nbr->iface, size); + + size -= LDP_HDR_SIZE; + + gen_msg_tlv(buf, MSG_TYPE_LABELABORTREQ, size); + + bufferevent_write(nbr->bev, buf->buf, buf->wpos); + + buf_free(buf); + + return (0); +} + +int +recv_labelabortreq(struct nbr *nbr, char *buf, u_int16_t len) +{ + struct ldp_msg *la; + u_int32_t messageid; + + log_debug("recv_labelabortreq: neighbor ID %s", inet_ntoa(nbr->id)); + + if (nbr->state != NBR_STA_OPER) + return (-1); + + la = (struct ldp_msg *)buf; + + if ((len - TLV_HDR_LEN) < ntohs(la->length)) { + /* XXX: send notification */ + return (-1); + } + + messageid = la->msgid; + + buf += sizeof(struct ldp_msg); + len -= sizeof(struct ldp_msg); + + nbr_fsm(nbr, NBR_EVT_PDU_RCVD); + + return (0); +} + +/* Other TLV related functions */ +void +gen_fec_tlv(struct buf *buf, u_int32_t prefix, u_int8_t prefixlen) +{ + struct fec_tlv ft; + u_int8_t type; + u_int16_t family; + u_int8_t len; + u_int32_t addr; + + ft.type = htons(TLV_TYPE_FEC); + ft.length = htons(sizeof(ft) + (int)(prefixlen/8)); + + buf_add(buf, &ft, sizeof(ft)); + + if (prefixlen == 32) + type = FEC_ADDRESS; + else + type = FEC_PREFIX; + family = htons(FEC_IPV4); + len = prefixlen; + addr = prefix; + + buf_add(buf, &type, sizeof(type)); + buf_add(buf, &family, sizeof(family)); + buf_add(buf, &len, sizeof(len)); + buf_add(buf, &addr, (int)(prefixlen/8)); +} + +void +gen_label_tlv(struct buf *buf, u_int32_t label) +{ + struct label_tlv lt; + + lt.type = htons(TLV_TYPE_GENERICLABEL); + lt.length = htons(sizeof(label)); + lt.label = htonl(label); + + buf_add(buf, <, sizeof(lt)); +} + +u_int32_t +tlv_decode_label(struct label_tlv *lt) +{ + if (lt->type != htons(TLV_TYPE_GENERICLABEL)) { + /* XXX: send notification */ + return (NO_LABEL); + } + + if (ntohs(lt->length) != sizeof(lt->label)) { + /* XXX: send notification */ + return (NO_LABEL); + } + + return (ntohl(lt->label)); +} + +int +validate_fec_elm(char *buf) +{ + u_int16_t *family; + + if (*buf != FEC_WILDCARD && *buf != FEC_PREFIX && *buf != + FEC_ADDRESS) { + /* XXX: send notification */ + return (-1); + } + + buf += sizeof(u_int8_t); + family = (u_int16_t *)buf; + + if (*family != htons(FEC_IPV4)) { + /* XXX: send notification */ + return (-1); + } + + return (0); +} + +u_int32_t +decode_fec_elm(char *buf) +{ + struct fec_elm *fe = (struct fec_elm *)buf; + + return (fe->addr); +} + +u_int8_t +decode_fec_len_elm(char *buf) +{ + /* Skip type and family */ + buf += sizeof(u_int8_t); + buf += sizeof(u_int16_t); + + return (*buf); +} diff --git a/usr.sbin/ldpd/lde.c b/usr.sbin/ldpd/lde.c new file mode 100644 index 00000000000..de86d890bdf --- /dev/null +++ b/usr.sbin/ldpd/lde.c @@ -0,0 +1,728 @@ +/* $OpenBSD: lde.c,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2004, 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004 Esben Norby <norby@openbsd.org> + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <err.h> +#include <errno.h> +#include <stdlib.h> +#include <signal.h> +#include <string.h> +#include <pwd.h> +#include <unistd.h> +#include <event.h> + +#include "ldp.h" +#include "ldpd.h" +#include "ldpe.h" +#include "log.h" +#include "lde.h" + +void lde_sig_handler(int sig, short, void *); +void lde_shutdown(void); +void lde_dispatch_imsg(int, short, void *); +void lde_dispatch_parent(int, short, void *); + +void lde_nbr_init(u_int32_t); +void lde_nbr_free(void); +struct lde_nbr *lde_nbr_find(u_int32_t); +struct lde_nbr *lde_nbr_new(u_int32_t, struct lde_nbr *); +void lde_nbr_del(struct lde_nbr *); + +void lde_address_list_free(struct lde_nbr *); +void lde_req_list_free(struct lde_nbr *); +void lde_map_list_free(struct lde_nbr *); + +struct ldpd_conf *ldeconf = NULL, *nconf = NULL; +struct imsgbuf *ibuf_ldpe; +struct imsgbuf *ibuf_main; +struct lde_nbr *nbrself; + +/* ARGSUSED */ +void +lde_sig_handler(int sig, short event, void *arg) +{ + /* + * signal handler rules don't apply, libevent decouples for us + */ + + switch (sig) { + case SIGINT: + case SIGTERM: + lde_shutdown(); + /* NOTREACHED */ + default: + fatalx("unexpected signal"); + } +} + +/* label decision engine */ +pid_t +lde(struct ldpd_conf *xconf, int pipe_parent2lde[2], int pipe_ldpe2lde[2], + int pipe_parent2ldpe[2]) +{ + struct event ev_sigint, ev_sigterm; + struct timeval now; + struct passwd *pw; + pid_t pid; + + switch (pid = fork()) { + case -1: + fatal("cannot fork"); + /* NOTREACHED */ + case 0: + break; + default: + return (pid); + } + + ldeconf = xconf; + + if ((pw = getpwnam(LDPD_USER)) == NULL) + fatal("getpwnam"); + + if (chroot(pw->pw_dir) == -1) + fatal("chroot"); + if (chdir("/") == -1) + fatal("chdir(\"/\")"); + + setproctitle("label decision engine"); + ldpd_process = PROC_LDE_ENGINE; + + if (setgroups(1, &pw->pw_gid) || + setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || + setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) + fatal("can't drop privileges"); + + event_init(); + lde_nbr_init(NBR_HASHSIZE); + + /* setup signal handler */ + signal_set(&ev_sigint, SIGINT, lde_sig_handler, NULL); + signal_set(&ev_sigterm, SIGTERM, lde_sig_handler, NULL); + signal_add(&ev_sigint, NULL); + signal_add(&ev_sigterm, NULL); + signal(SIGPIPE, SIG_IGN); + signal(SIGHUP, SIG_IGN); + + /* setup pipes */ + close(pipe_ldpe2lde[0]); + close(pipe_parent2lde[0]); + close(pipe_parent2ldpe[0]); + close(pipe_parent2ldpe[1]); + + if ((ibuf_ldpe = malloc(sizeof(struct imsgbuf))) == NULL || + (ibuf_main = malloc(sizeof(struct imsgbuf))) == NULL) + fatal(NULL); + imsg_init(ibuf_ldpe, pipe_ldpe2lde[1], lde_dispatch_imsg); + imsg_init(ibuf_main, pipe_parent2lde[1], lde_dispatch_parent); + + /* setup event handler */ + ibuf_ldpe->events = EV_READ; + event_set(&ibuf_ldpe->ev, ibuf_ldpe->fd, ibuf_ldpe->events, + ibuf_ldpe->handler, ibuf_ldpe); + event_add(&ibuf_ldpe->ev, NULL); + + ibuf_main->events = EV_READ; + event_set(&ibuf_main->ev, ibuf_main->fd, ibuf_main->events, + ibuf_main->handler, ibuf_main); + event_add(&ibuf_main->ev, NULL); + + rt_init(); + + gettimeofday(&now, NULL); + ldeconf->uptime = now.tv_sec; + + event_dispatch(); + + lde_shutdown(); + /* NOTREACHED */ + + return (0); +} + +void +lde_shutdown(void) +{ + rt_clear(); + + lde_nbr_free(); + + msgbuf_clear(&ibuf_ldpe->w); + free(ibuf_ldpe); + msgbuf_clear(&ibuf_main->w); + free(ibuf_main); + free(ldeconf); + + log_info("label decision engine exiting"); + _exit(0); +} + +int +lde_imsg_compose_ldpe(int type, u_int32_t peerid, pid_t pid, void *data, + u_int16_t datalen) +{ + return (imsg_compose(ibuf_ldpe, type, peerid, pid, data, datalen)); +} + +/* ARGSUSED */ +void +lde_dispatch_imsg(int fd, short event, void *bula) +{ + struct imsgbuf *ibuf = bula; + struct imsg imsg; + struct lde_nbr rn, *nbr; + struct map map; + struct timespec tp; + struct in_addr addr; + ssize_t n; + time_t now; + int state, shut = 0; + + if (event & EV_READ) { + if ((n = imsg_read(ibuf)) == -1) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + shut = 1; + } + if (event & EV_WRITE) { + if (msgbuf_write(&ibuf->w) == -1) + fatal("msgbuf_write"); + } + + clock_gettime(CLOCK_MONOTONIC, &tp); + now = tp.tv_sec; + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("lde_dispatch_imsg: imsg_read error"); + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_LABEL_MAPPING: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(map)) + fatalx("invalid size of OE request"); + memcpy(&map, imsg.data, sizeof(map)); + + nbr = lde_nbr_find(imsg.hdr.peerid); + if (nbr == NULL) { + log_debug("lde_dispatch_imsg: cannot find " + "lde neighbor"); + return; + } + + lde_check_mapping(&map, nbr); + break; + case IMSG_LABEL_MAPPING_FULL: + rt_snap(imsg.hdr.peerid); + + lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, + imsg.hdr.peerid, 0, 0, 0); + + break; + case IMSG_LABEL_REQUEST: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(map)) + fatalx("invalid size of OE request"); + memcpy(&map, imsg.data, sizeof(map)); + + nbr = lde_nbr_find(imsg.hdr.peerid); + if (nbr == NULL) { + log_debug("lde_dispatch_imsg: cannot find " + "lde neighbor"); + return; + } + + lde_check_request(&map, nbr); + break; + case IMSG_ADDRESS_ADD: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(addr)) + fatalx("invalid size of OE request"); + memcpy(&addr, imsg.data, sizeof(addr)); + + nbr = lde_nbr_find(imsg.hdr.peerid); + if (nbr == NULL) { + log_debug("lde_dispatch_imsg: cannot find " + "lde neighbor"); + return; + } + + if (lde_address_add(nbr, &addr) < 0) { + log_debug("lde_dispatch_imsg: cannot add " + "address %s, it already exists", + inet_ntoa(addr)); + } + + break; + case IMSG_ADDRESS_DEL: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(addr)) + fatalx("invalid size of OE request"); + memcpy(&addr, imsg.data, sizeof(addr)); + + nbr = lde_nbr_find(imsg.hdr.peerid); + if (nbr == NULL) { + log_debug("lde_dispatch_imsg: cannot find " + "lde neighbor"); + return; + } + + if (lde_address_del(nbr, &addr) < 0) { + log_debug("lde_dispatch_imsg: cannot delete " + "address %s, it does not exists", + inet_ntoa(addr)); + } + + break; + case IMSG_NEIGHBOR_UP: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(rn)) + fatalx("invalid size of OE request"); + memcpy(&rn, imsg.data, sizeof(rn)); + + if (lde_nbr_find(imsg.hdr.peerid)) + fatalx("lde_dispatch_imsg: " + "neighbor already exists"); + lde_nbr_new(imsg.hdr.peerid, &rn); + break; + case IMSG_NEIGHBOR_DOWN: + lde_nbr_del(lde_nbr_find(imsg.hdr.peerid)); + break; + case IMSG_NEIGHBOR_CHANGE: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(state)) + fatalx("invalid size of OE request"); + memcpy(&state, imsg.data, sizeof(state)); + + nbr = lde_nbr_find(imsg.hdr.peerid); + if (nbr == NULL) + break; + + nbr->state = state; + break; + case IMSG_CTL_SHOW_LIB: + rt_dump(imsg.hdr.pid); + + imsg_compose(ibuf_ldpe, IMSG_CTL_END, 0, imsg.hdr.pid, + NULL, 0); + break; + default: + log_debug("lde_dispatch_imsg: unexpected imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + if (!shut) + imsg_event_add(ibuf); + else { + /* this pipe is dead, so remove the event handler */ + event_del(&ibuf->ev); + event_loopexit(NULL); + } +} + +/* ARGSUSED */ +void +lde_dispatch_parent(int fd, short event, void *bula) +{ + struct imsg imsg; + struct kroute kr; + struct rroute rr; + struct imsgbuf *ibuf = bula; + ssize_t n; + int shut = 0; + + if (event & EV_READ) { + if ((n = imsg_read(ibuf)) == -1) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + shut = 1; + } + if (event & EV_WRITE) { + if (msgbuf_write(&ibuf->w) == -1) + fatal("msgbuf_write"); + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("lde_dispatch_parent: imsg_read error"); + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_NETWORK_ADD: + if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(rr)) { + log_warnx("lde_dispatch_parent: " + "wrong imsg len"); + break; + } + memcpy(&rr, imsg.data, sizeof(rr)); + + lde_insert(&rr.kr); + + break; + case IMSG_NETWORK_DEL: + if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(rr)) { + log_warnx("lde_dispatch_parent: " + "wrong imsg len"); + break; + } + memcpy(&rr, imsg.data, sizeof(rr)); + + break; + case IMSG_KROUTE_GET: + if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(kr)) { + log_warnx("lde_dispatch_parent: " + "wrong imsg len"); + break; + } + memcpy(&kr, imsg.data, sizeof(kr)); + +/* if ((rn = rt_find(kr.prefix.s_addr, kr.prefixlen, + DT_NET)) != NULL) + lde_send_change_kroute(rn); + else*/ + /* should not happen */ + imsg_compose(ibuf_main, IMSG_KLABEL_DELETE, 0, + 0, &kr, sizeof(kr)); + break; + case IMSG_RECONF_CONF: + if ((nconf = malloc(sizeof(struct ldpd_conf))) == + NULL) + fatal(NULL); + memcpy(nconf, imsg.data, sizeof(struct ldpd_conf)); + + break; + case IMSG_RECONF_AREA: + break; + case IMSG_RECONF_IFACE: + break; + case IMSG_RECONF_END: + break; + default: + log_debug("lde_dispatch_parent: unexpected imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + if (!shut) + imsg_event_add(ibuf); + else { + /* this pipe is dead, so remove the event handler */ + event_del(&ibuf->ev); + event_loopexit(NULL); + } +} + +u_int32_t +lde_router_id(void) +{ + return (ldeconf->rtr_id.s_addr); +} + +void +lde_send_insert_klabel(struct rt_node *r) +{ + struct kroute kr; + + bzero(&kr, sizeof(kr)); + kr.prefix.s_addr = r->prefix.s_addr; + kr.nexthop.s_addr = r->nexthop.s_addr; + kr.local_label = r->local_label; + kr.remote_label = r->remote_label; + kr.prefixlen = r->prefixlen; + kr.ext_tag = r->ext_tag; + + imsg_compose(ibuf_main, IMSG_KLABEL_INSERT, 0, 0, &kr, sizeof(kr)); +} + +void +lde_send_change_klabel(struct rt_node *r) +{ + struct kroute kr; + + bzero(&kr, sizeof(kr)); + kr.prefix.s_addr = r->prefix.s_addr; + kr.nexthop.s_addr = r->nexthop.s_addr; + kr.local_label = r->local_label; + kr.remote_label = r->remote_label; + kr.prefixlen = r->prefixlen; + kr.ext_tag = r->ext_tag; + + imsg_compose(ibuf_main, IMSG_KLABEL_CHANGE, 0, 0, &kr, sizeof(kr)); +} + +void +lde_send_delete_klabel(struct rt_node *r) +{ + struct kroute kr; + + bzero(&kr, sizeof(kr)); + kr.prefix.s_addr = r->prefix.s_addr; + kr.prefixlen = r->prefixlen; + + imsg_compose(ibuf_main, IMSG_KLABEL_DELETE, 0, 0, &kr, sizeof(kr)); +} + +void +lde_send_labelrequest(u_int32_t peerid, struct map *map) +{ + imsg_compose(ibuf_ldpe, IMSG_REQUEST_ADD, peerid, 0, map, sizeof(map)); + imsg_compose(ibuf_ldpe, IMSG_REQUEST_ADD_END, peerid, 0, NULL, 0); +} + +void +lde_send_labelmapping(u_int32_t peerid, struct map *map) +{ + imsg_compose(ibuf_ldpe, IMSG_MAPPING_ADD, peerid, 0, map, sizeof(map)); + imsg_compose(ibuf_ldpe, IMSG_MAPPING_ADD_END, peerid, 0, NULL, 0); +} + +void +lde_send_labelrelease(u_int32_t peerid, struct map *map) +{ + imsg_compose(ibuf_ldpe, IMSG_RELEASE_ADD, peerid, 0, map, sizeof(map)); + imsg_compose(ibuf_ldpe, IMSG_RELEASE_ADD_END, peerid, 0, NULL, 0); +} + +void +lde_send_notification(u_int32_t peerid, u_int32_t code) +{ + imsg_compose(ibuf_ldpe, IMSG_NOTIFICATION_SEND, peerid, 0, &code, + sizeof(u_int32_t)); +} + +LIST_HEAD(lde_nbr_head, lde_nbr); + +struct nbr_table { + struct lde_nbr_head *hashtbl; + u_int32_t hashmask; +} ldenbrtable; + +#define LDE_NBR_HASH(x) \ + &ldenbrtable.hashtbl[(x) & ldenbrtable.hashmask] + +void +lde_nbr_init(u_int32_t hashsize) +{ + struct lde_nbr_head *head; + u_int32_t hs, i; + + for (hs = 1; hs < hashsize; hs <<= 1) + ; + ldenbrtable.hashtbl = calloc(hs, sizeof(struct lde_nbr_head)); + if (ldenbrtable.hashtbl == NULL) + fatal("lde_nbr_init"); + + for (i = 0; i < hs; i++) + LIST_INIT(&ldenbrtable.hashtbl[i]); + + ldenbrtable.hashmask = hs - 1; + + if ((nbrself = calloc(1, sizeof(*nbrself))) == NULL) + fatal("lde_nbr_init"); + + nbrself->id.s_addr = lde_router_id(); + nbrself->peerid = NBR_IDSELF; + nbrself->state = NBR_STA_DOWN; + nbrself->self = 1; + head = LDE_NBR_HASH(NBR_IDSELF); + LIST_INSERT_HEAD(head, nbrself, hash); +} + +void +lde_nbr_free(void) +{ + free(nbrself); + free(ldenbrtable.hashtbl); +} + +struct lde_nbr * +lde_nbr_find(u_int32_t peerid) +{ + struct lde_nbr_head *head; + struct lde_nbr *nbr; + + head = LDE_NBR_HASH(peerid); + + LIST_FOREACH(nbr, head, hash) { + if (nbr->peerid == peerid) + return (nbr); + } + + return (NULL); +} + +struct lde_nbr * +lde_nbr_new(u_int32_t peerid, struct lde_nbr *new) +{ + struct lde_nbr_head *head; + struct lde_nbr *nbr; + struct iface *iface; + + if (lde_nbr_find(peerid)) + return (NULL); + + if ((nbr = calloc(1, sizeof(*nbr))) == NULL) + fatal("lde_nbr_new"); + + memcpy(nbr, new, sizeof(*nbr)); + nbr->peerid = peerid; + + TAILQ_INIT(&nbr->addr_list); + TAILQ_INIT(&nbr->req_list); + TAILQ_INIT(&nbr->sent_map_list); + TAILQ_INIT(&nbr->recv_map_list); + + head = LDE_NBR_HASH(peerid); + LIST_INSERT_HEAD(head, nbr, hash); + + LIST_FOREACH(iface, &ldeconf->iface_list, entry) { + if (iface->ifindex == new->ifindex) { + LIST_INSERT_HEAD(&iface->lde_nbr_list, nbr, entry); + break; + } + } + + return (nbr); +} + +int +lde_address_add(struct lde_nbr *lr, struct in_addr *addr) +{ + struct lde_nbr_address *address; + + if (lde_address_find(lr, addr) != NULL) + return (-1); + + if ((address = calloc(1, sizeof(*address))) == NULL) + fatal("lde_address_add"); + + address->addr.s_addr = addr->s_addr; + + TAILQ_INSERT_TAIL(&lr->addr_list, address, entry); + + log_debug("lde_address_add: added %s", inet_ntoa(*addr)); + + return (0); +} + +struct lde_nbr_address * +lde_address_find(struct lde_nbr *lr, struct in_addr *addr) +{ + struct lde_nbr_address *address = NULL; + + TAILQ_FOREACH(address, &lr->addr_list, entry) { + if (address->addr.s_addr == addr->s_addr) + return (address); + } + + return (NULL); +} + +int +lde_address_del(struct lde_nbr *lr, struct in_addr *addr) +{ + struct lde_nbr_address *address; + + address = lde_address_find(lr, addr); + if (address == NULL) + return (-1); + + TAILQ_REMOVE(&lr->addr_list, address, entry); + + free(address); + + log_debug("lde_address_del: deleted %s", inet_ntoa(*addr)); + + return (0); +} + +void +lde_address_list_free(struct lde_nbr *nbr) +{ + struct lde_nbr_address *addr; + + while ((addr = TAILQ_FIRST(&nbr->addr_list)) != NULL) { + TAILQ_REMOVE(&nbr->addr_list, addr, entry); + free(addr); + } +} + +void +lde_req_list_free(struct lde_nbr *nbr) +{ + struct lde_req_entry *req; + + while ((req = TAILQ_FIRST(&nbr->req_list)) != NULL) { + TAILQ_REMOVE(&nbr->req_list, req, entry); + free(req); + } +} + +void +lde_map_list_free(struct lde_nbr *nbr) +{ + struct lde_map_entry *map; + + while ((map = TAILQ_FIRST(&nbr->recv_map_list)) != NULL) { + TAILQ_REMOVE(&nbr->recv_map_list, map, entry); + free(map); + } + + while ((map = TAILQ_FIRST(&nbr->sent_map_list)) != NULL) { + TAILQ_REMOVE(&nbr->sent_map_list, map, entry); + free(map); + } +} + +void +lde_nbr_del(struct lde_nbr *nbr) +{ + if (nbr == NULL) + return; + + lde_req_list_free(nbr); + lde_map_list_free(nbr); + lde_address_list_free(nbr); + + LIST_REMOVE(nbr, hash); + LIST_REMOVE(nbr, entry); + + free(nbr); +} + +struct lde_nbr * +lde_find_address(struct in_addr address) +{ + struct iface *iface; + struct lde_nbr *ln; + + LIST_FOREACH(iface, &ldeconf->iface_list, entry) { + LIST_FOREACH(ln, &iface->lde_nbr_list, entry) { + if (lde_address_find(ln, &address) != NULL) + return (ln); + } + } + + return (NULL); +} diff --git a/usr.sbin/ldpd/lde.h b/usr.sbin/ldpd/lde.h new file mode 100644 index 00000000000..7ba1b09a3ca --- /dev/null +++ b/usr.sbin/ldpd/lde.h @@ -0,0 +1,127 @@ +/* $OpenBSD: lde.h,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LDE_H_ +#define _LDE_H_ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/tree.h> +#include <sys/queue.h> +#include <event.h> +#include <limits.h> + +/* Label mapping request pending */ +struct lde_req_entry { + TAILQ_ENTRY(lde_req_entry) entry; + struct in_addr prefix; + u_int8_t prefixlen; +}; + +/* Label mapping message sent */ +struct lde_map_entry { + TAILQ_ENTRY(lde_map_entry) entry; + struct in_addr prefix; + u_int8_t prefixlen; + u_int32_t label; +}; + +/* Addresses belonging to neighbor */ +struct lde_nbr_address { + TAILQ_ENTRY(lde_nbr_address) entry; + struct in_addr addr; +}; + +/* just the info LDE needs */ +struct lde_nbr { + LIST_ENTRY(lde_nbr) hash, entry; + struct in_addr id; + + TAILQ_HEAD(, lde_req_entry) req_list; + TAILQ_HEAD(, lde_map_entry) recv_map_list; + TAILQ_HEAD(, lde_map_entry) sent_map_list; + TAILQ_HEAD(, lde_nbr_address) addr_list; + + u_int32_t peerid; + unsigned int ifindex; + int self; + int state; + + u_int16_t lspace; +}; + +struct rt_label { + TAILQ_ENTRY(rt_label) entry; + u_int32_t label; + struct lde_nbr *nexthop; +}; + +struct rt_node { + RB_ENTRY(rt_node) entry; + TAILQ_HEAD(, rt_label) labels_list; + struct in_addr prefix; + struct in_addr nexthop; + + u_int32_t local_label; + u_int32_t remote_label; + + u_int32_t ext_tag; + u_int16_t lspace; + u_int8_t flags; + u_int8_t prefixlen; + u_int8_t invalid; +}; + +/* lde.c */ +pid_t lde(struct ldpd_conf *, int [2], int [2], int [2]); +int lde_imsg_compose_ldpe(int, u_int32_t, pid_t, void *, + u_int16_t); +u_int32_t lde_router_id(void); +void lde_send_insert_klabel(struct rt_node *); +void lde_send_change_klabel(struct rt_node *); +void lde_send_delete_klabel(struct rt_node *); +void lde_send_labelmapping(u_int32_t, struct map *); +void lde_send_labelrequest(u_int32_t, struct map *); +void lde_send_labelrelease(u_int32_t, struct map *); +void lde_send_notification(u_int32_t, u_int32_t); + +void lde_nbr_del(struct lde_nbr *); +struct lde_nbr *lde_find_address(struct in_addr); + + +int lde_address_add(struct lde_nbr *, struct in_addr *); +struct lde_nbr_address *lde_address_find(struct lde_nbr *, struct in_addr *); +int lde_address_del(struct lde_nbr *, struct in_addr *); + +/* lde_lib.c */ +void rt_init(void); +int rt_compare(struct rt_node *, struct rt_node *); +int rt_insert(struct rt_node *); +int rt_remove(struct rt_node *); +struct rt_node *rt_find(in_addr_t, u_int8_t); +void rt_clear(void); +void route_reset_timers(struct rt_node *); +int route_start_timeout(struct rt_node *); +void route_start_garbage(struct rt_node *); +void rt_dump(pid_t); +void rt_snap(u_int32_t); +void lde_insert(struct kroute *); +void lde_check_mapping(struct map *, struct lde_nbr *); +void lde_check_request(struct map *, struct lde_nbr *); + +#endif /* _LDE_H_ */ diff --git a/usr.sbin/ldpd/lde_lib.c b/usr.sbin/ldpd/lde_lib.c new file mode 100644 index 00000000000..ef99606a1ce --- /dev/null +++ b/usr.sbin/ldpd/lde_lib.c @@ -0,0 +1,367 @@ +/* $OpenBSD: lde_lib.c,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <net/if.h> +#include <net/if_types.h> +#include <netmpls/mpls.h> +#include <ctype.h> +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <event.h> + +#include "ldpd.h" +#include "ldp.h" +#include "log.h" +#include "lde.h" + +extern struct ldpd_conf *ldeconf; +RB_HEAD(rt_tree, rt_node) rt; +RB_PROTOTYPE(rt_tree, rt_node, entry, rt_compare) +RB_GENERATE(rt_tree, rt_node, entry, rt_compare) + +u_int32_t lde_assign_label(void); + +/* route table */ +void +rt_init(void) +{ + RB_INIT(&rt); +} + +int +rt_compare(struct rt_node *a, struct rt_node *b) +{ + if (ntohl(a->prefix.s_addr) < ntohl(b->prefix.s_addr)) + return (-1); + if (ntohl(a->prefix.s_addr) > ntohl(b->prefix.s_addr)) + return (1); + if (a->prefixlen < b->prefixlen) + return (-1); + if (a->prefixlen > b->prefixlen) + return (1); + + return (0); +} + +struct rt_node * +rt_find(in_addr_t prefix, u_int8_t prefixlen) +{ + struct rt_node s; + + s.prefix.s_addr = prefix; + s.prefixlen = prefixlen; + + return (RB_FIND(rt_tree, &rt, &s)); +} + +int +rt_insert(struct rt_node *r) +{ + if (RB_INSERT(rt_tree, &rt, r) != NULL) { + log_warnx("rt_insert failed for %s/%u", + inet_ntoa(r->prefix), r->prefixlen); + free(r); + return (-1); + } + + return (0); +} + +int +rt_remove(struct rt_node *r) +{ + if (RB_REMOVE(rt_tree, &rt, r) == NULL) { + log_warnx("rt_remove failed for %s/%u", + inet_ntoa(r->prefix), r->prefixlen); + return (-1); + } + + free(r); + return (0); +} + +void +rt_dump(pid_t pid) +{ + struct rt_node *r; + static struct ctl_rt rtctl; + + RB_FOREACH(r, rt_tree, &rt) { + rtctl.prefix.s_addr = r->prefix.s_addr; + rtctl.prefixlen = r->prefixlen; + rtctl.nexthop.s_addr = r->nexthop.s_addr; + rtctl.flags = r->flags; + rtctl.local_label = r->local_label; + rtctl.remote_label = r->remote_label; + + lde_imsg_compose_ldpe(IMSG_CTL_SHOW_LIB, 0, pid, &rtctl, + sizeof(rtctl)); + } +} + +void +rt_snap(u_int32_t peerid) +{ + struct rt_node *r; + struct map map; + + bzero(&map, sizeof(map)); + + RB_FOREACH(r, rt_tree, &rt) { + map.prefix = r->prefix.s_addr; + map.prefixlen = r->prefixlen; + map.label = (ntohl(r->local_label) & MPLS_LABEL_MASK) >> + MPLS_LABEL_OFFSET; + + lde_imsg_compose_ldpe(IMSG_MAPPING_ADD, peerid, 0, &map, + sizeof(map)); + } +} + +void +rt_clear(void) +{ + struct rt_node *r; + + while ((r = RB_MIN(rt_tree, &rt)) != NULL) + rt_remove(r); +} + +u_int32_t +lde_assign_label() +{ + static u_int32_t label = MPLS_LABEL_RESERVED_MAX; + + /* XXX some checks needed */ + label++; + return (htonl(label << MPLS_LABEL_OFFSET)); +} + +void +lde_insert(struct kroute *kr) +{ + struct rt_node *rn; + + rn = calloc(1, sizeof(*rn)); + if (rn == NULL) + fatal("lde_insert"); + + rn->prefix.s_addr = kr->prefix.s_addr; + rn->nexthop.s_addr = kr->nexthop.s_addr; + rn->prefixlen = kr->prefixlen; + TAILQ_INIT(&rn->labels_list); + + rt_insert(rn); + + if (kr->local_label == 0) { + /* Directly connected route */ + if (kr->nexthop.s_addr == INADDR_ANY) { + rn->local_label = + htonl(MPLS_LABEL_IMPLNULL << MPLS_LABEL_OFFSET); + rn->nexthop.s_addr = htonl(INADDR_LOOPBACK); + } else + rn->local_label = lde_assign_label(); + + lde_send_insert_klabel(rn); + } else + rn->local_label = (htonl(kr->local_label) << MPLS_LABEL_OFFSET); +} + +void +lde_check_mapping(struct map *map, struct lde_nbr *ln) +{ + struct rt_node *rn; + struct rt_label *rl; + struct lde_nbr_address *addr; + struct lde_map_entry *me, *menew; + struct lde_req_entry *req; + struct iface *iface; + struct map localmap; + + rn = rt_find(map->prefix, map->prefixlen); + if (rn == NULL) + return; + + TAILQ_FOREACH(me, &ln->recv_map_list, entry) { + if (me->prefix.s_addr == map->prefix && + me->prefixlen == map->prefixlen) { + if (me->label == map->label) { + lde_send_labelrelease(ln->peerid, map); + return; + } + } + } + + addr = lde_address_find(ln, &rn->nexthop); + if (addr == NULL) { + if (ldeconf->mode & MODE_RET_CONSERVATIVE) { + lde_send_labelrelease(ln->peerid, map); + return; + } + + rl = calloc(1, sizeof(*rl)); + if (rl == NULL) + fatal("lde_check_mapping"); + + rl->label = map->label; + rl->nexthop = ln; + + TAILQ_INSERT_TAIL(&rn->labels_list, rl, entry); + return; + } + + rn->remote_label = htonl(map->label << MPLS_LABEL_OFFSET); + + /* If we are ingress for this LSP install the label */ + if (rn->nexthop.s_addr == INADDR_ANY) + lde_send_change_klabel(rn); + + /* Record the mapping from this peer */ + menew = calloc(1, sizeof(*menew)); + if (menew == NULL) + fatal("lde_check_mapping"); + + menew->prefix.s_addr = map->prefix; + menew->prefixlen = map->prefixlen; + menew->label = map->label; + + TAILQ_INSERT_HEAD(&ln->recv_map_list, menew, entry); + + /* Redistribute the current mapping to every nbr */ + localmap.label = rn->local_label; + localmap.prefix = rn->prefix.s_addr; + localmap.prefixlen = rn->prefixlen; + + LIST_FOREACH(iface, &ldeconf->iface_list, entry) { + LIST_FOREACH(ln, &iface->lde_nbr_list, entry) { + /* Did we already send a mapping to this peer? */ + TAILQ_FOREACH(me, &ln->sent_map_list, entry) { + if (me->prefix.s_addr == rn->prefix.s_addr && + me->prefixlen == rn->prefixlen) + break; + } + if (me != NULL) { + /* XXX: check RAttributes */ + continue; + } + + if (ldeconf->mode & MODE_ADV_UNSOLICITED && + ldeconf->mode & MODE_DIST_ORDERED) { + lde_send_labelmapping(ln->peerid, &localmap); + + menew = calloc(1, sizeof(*menew)); + if (menew == NULL) + fatal("lde_check_mapping"); + + menew->prefix.s_addr = map->prefix; + menew->prefixlen = map->prefixlen; + menew->label = map->label; + + TAILQ_INSERT_HEAD(&ln->sent_map_list, menew, + entry); + } + + TAILQ_FOREACH(req, &ln->req_list, entry) { + if (req->prefix.s_addr == rn->prefix.s_addr && + req->prefixlen == rn->prefixlen) + break; + } + if (req != NULL) { + lde_send_labelmapping(ln->peerid, &localmap); + + menew = calloc(1, sizeof(*menew)); + if (menew == NULL) + fatal("lde_check_mapping"); + + menew->prefix.s_addr = map->prefix; + menew->prefixlen = map->prefixlen; + menew->label = map->label; + + TAILQ_INSERT_HEAD(&ln->sent_map_list, menew, + entry); + + TAILQ_REMOVE(&ln->req_list, req, entry); + free(req); + } + } + } + + lde_send_change_klabel(rn); +} + +void +lde_check_request(struct map *map, struct lde_nbr *ln) +{ + struct lde_req_entry *lre, *newlre; + struct rt_node *rn; + struct lde_nbr *lnn; + struct map localmap; + + bzero(&newlre, sizeof(newlre)); + + rn = rt_find(map->prefix, map->prefixlen); + if (rn == NULL || rn->remote_label == NO_LABEL) { + lde_send_notification(ln->peerid, S_NO_ROUTE); + return; + } + + if (lde_address_find(ln, &rn->nexthop)) { + lde_send_notification(ln->peerid, S_LOOP_DETECTED); + return; + } + + TAILQ_FOREACH(lre, &ln->req_list, entry) { + if (lre->prefix.s_addr == map->prefix && + lre->prefixlen == map->prefixlen) + return; + } + + /* XXX: if we are egress ? */ + if (rn->remote_label != NO_LABEL) { + bzero(&localmap, sizeof(localmap)); + localmap.prefix = map->prefix; + localmap.prefixlen = map->prefixlen; + localmap.label = rn->local_label; + + lde_send_labelmapping(ln->peerid, &localmap); + } else { + lnn = lde_find_address(rn->nexthop); + if (lnn == NULL) + return; + + lde_send_labelrequest(lnn->peerid, map); + + newlre = calloc(1, sizeof(*newlre)); + if (newlre == NULL) + fatal("lde_check_request"); + + newlre->prefix.s_addr = map->prefix; + newlre->prefixlen = map->prefixlen; + + TAILQ_INSERT_HEAD(&ln->req_list, newlre, entry); + } +} diff --git a/usr.sbin/ldpd/ldp.h b/usr.sbin/ldpd/ldp.h new file mode 100644 index 00000000000..7bcf3bccbec --- /dev/null +++ b/usr.sbin/ldpd/ldp.h @@ -0,0 +1,220 @@ +/* $OpenBSD: ldp.h,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* LDP protocol definitions */ + +#ifndef _LDP_H_ +#define _LDP_H_ + +#include <netinet/in.h> + +/* misc */ +#define LDP_VERSION 1 +#define LDP_PORT 646 +#define AllRouters "224.0.0.2" + +#define LDP_MAX_LEN 4096 + +#define INFINITE_TMR (-1) +#define LINK_DFLT_HOLDTIME 15 +#define TARGETED_DFLT_HOLDTIME 45 + +#define DEFAULT_HOLDTIME 15 +#define MIN_HOLDTIME 1 +#define MAX_HOLDTIME 0xffff + +#define DEFAULT_KEEPALIVE 180 +#define MIN_KEEPALIVE 1 +#define MAX_KEEPALIVE 0xffff +#define KEEPALIVE_PER_PERIOD 3 + +#define DEFAULT_HELLO_INTERVAL 5 +#define MIN_HELLO_INTERVAL 1 +#define MAX_HELLO_INTERVAL 0xffff /* XXX */ + +#define INIT_DELAY_TMR 15 +#define DEFAULT_NBR_TMOUT 86400 /* 24 hours */ + +/* LDP message types */ +#define MSG_TYPE_NOTIFICATION 0x0001 +#define MSG_TYPE_HELLO 0x0100 +#define MSG_TYPE_INIT 0x0200 +#define MSG_TYPE_KEEPALIVE 0x0201 +#define MSG_TYPE_ADDR 0x0300 +#define MSG_TYPE_ADDRWITHDRAW 0x0301 +#define MSG_TYPE_LABELMAPPING 0x0400 +#define MSG_TYPE_LABELREQUEST 0x0401 +#define MSG_TYPE_LABELWITHDRAW 0x0402 +#define MSG_TYPE_LABELRELEASE 0x0403 +#define MSG_TYPE_LABELABORTREQ 0x0404 + +/* LDP TLV types */ +#define TLV_TYPE_FEC 0x0100 +#define TLV_TYPE_ADDRLIST 0x0101 +#define TLV_TYPE_HOPCOUNT 0x0103 +#define TLV_TYPE_PATHVECTOR 0x0104 +#define TLV_TYPE_GENERICLABEL 0x0200 +#define TLV_TYPE_ATMLABEL 0x0201 +#define TLV_TYPE_FRLABEL 0x0202 +#define TLV_TYPE_STATUS 0x0300 +#define TLV_TYPE_EXTSTATUS 0x0301 +#define TLV_TYPE_RETURNEDPDU 0x0302 +#define TLV_TYPE_RETURNEDMSG 0x0303 +#define TLV_TYPE_COMMONHELLO 0x0400 +#define TLV_TYPE_IPV4TRANSADDR 0x0401 +#define TLV_TYPE_CONFIG 0x0402 +#define TLV_TYPE_IPV6TRANSADDR 0x0403 +#define TLV_TYPE_COMMONSESSION 0x0500 +#define TLV_TYPE_ATMSESSIONPAR 0x0501 +#define TLV_TYPE_FRSESSION 0x0502 +#define TLV_TYPE_LABELREQUEST 0x0600 + +/* LDP header */ +struct ldp_hdr { + u_int16_t version; + u_int16_t length; + u_int32_t lsr_id; + u_int16_t lspace_id; + u_int8_t pad; +}; + +#define PDU_HDR_SIZE 4 + +#define LDP_HDR_SIZE 10 +#define INFINITE_HOLDTIME 0xffff + +/* TLV record */ +struct ldp_msg { + u_int16_t type; + u_int16_t length; + u_int32_t msgid; + /* Mandatory Parameters */ + /* Optional Parameters */ +}; + +#define LDP_MSG_LEN 6 +#define TLV_HDR_LEN 4 + +#define UNKNOWN_FLAGS_MASK 0xc000 +#define UNKNOWN_FLAG 0x8000 +#define FORWARD_FLAG 0xc000 + +#define TARGETED_HELLO 0x8000 +#define REQUEST_TARG_HELLO 0x4000 + +struct hello_prms_tlv { + u_int16_t type; + u_int16_t length; + u_int16_t holdtime; + u_int16_t reserved; +}; + +#define S_SUCCESS 0x00000000 +#define S_BAD_LDP_ID 0x80000001 +#define S_BAD_PROTO_VER 0x80000002 +#define S_BAD_PDU_LEN 0x80000003 +#define S_UNKNOWN_MSG 0x00000004 +#define S_BAD_MSG_LEN 0x80000005 +#define S_UNKNOWN_TLV 0x00000006 +#define S_BAD_TLV_LEN 0x80000007 +#define S_BAD_TLV_VAL 0x80000008 +#define S_HOLDTIME_EXP 0x80000009 +#define S_SHUTDOWN 0x8000000A +#define S_LOOP_DETECTED 0x0000000B +#define S_UNKNOWN_FEC 0x0000000C +#define S_NO_ROUTE 0x0000000D +#define S_NO_LABEL_RES 0x0000000E +#define S_AVAILABLE 0x0000000F +#define S_NO_HELLO 0x80000010 +#define S_PARM_ADV_MODE 0x80000011 +#define S_MAX_PDU_LEN 0x80000012 +#define S_PARM_L_RANGE 0x80000013 +#define S_KEEPALIVE_TMR 0x80000014 +#define S_LAB_REQ_ABRT 0x00000015 +#define S_MISS_MSG 0x00000016 +#define S_UNSUP_ADDR 0x00000017 + +struct sess_prms_tlv { + u_int16_t type; + u_int16_t length; + u_int16_t proto_version; + u_int16_t keepalive_time; + u_int8_t reserved; + u_int8_t pvlim; + u_int16_t max_pdu_len; + u_int32_t lsr_id; + u_int16_t lspace_id; +}; + +#define SESS_PRMS_SIZE 18 + +struct status_tlv { + u_int16_t type; + u_int16_t length; + u_int32_t status_code; + u_int32_t msg_id; + u_int16_t msg_type; +}; + +#define STATUS_SIZE 14 +#define STATUS_TLV_LEN 10 +#define STATUS_FATAL 0x80000000 + +struct address_list_tlv { + u_int16_t type; + u_int16_t length; + u_int16_t family; + /* address entries */ +}; + +#define BASIC_LABEL_MAP_LEN 24 + +#define ADDR_IPV4 0x1 +#define ADDR_IPV6 0x2 + +struct fec_tlv { + u_int16_t type; + u_int16_t length; + /* fec elm entries */ +}; + +/* This struct is badly aligned so use two 32 bit fields */ +struct fec_elm { + u_int32_t hdr; + u_int32_t addr; +}; + +#define FEC_ELM_MIN_LEN 4 + +#define FEC_WILDCARD 0x01 +#define FEC_PREFIX 0x02 +#define FEC_ADDRESS 0x03 + +#define FEC_IPV4 0x0001 + +struct label_tlv { + u_int16_t type; + u_int16_t length; + u_int32_t label; +}; + +#define LABEL_TLV_LEN 8 +#define NO_LABEL UINT_MAX + +#endif /* !_LDP_H_ */ diff --git a/usr.sbin/ldpd/ldpd.8 b/usr.sbin/ldpd/ldpd.8 new file mode 100644 index 00000000000..d9b340cd300 --- /dev/null +++ b/usr.sbin/ldpd/ldpd.8 @@ -0,0 +1,96 @@ +.\" $OpenBSD: ldpd.8,v 1.1 2009/06/01 20:59:45 michele Exp $ +.\" +.\" Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> +.\" Copyright (c) 2004, 2005, 2006 Esben Norby <norby@openbsd.org> +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: June 1 2009 $ +.Dt LDPD 8 +.Os +.Sh NAME +.Nm ldpd +.Nd "Label Distribution Protocol daemon" +.Sh SYNOPSIS +.Nm +.Op Fl dnv +.Oo Xo +.Fl D Ar macro Ns = Ns Ar value Oc +.Xc +.Op Fl f Ar file +.Sh DESCRIPTION +.Nm +is the Label Distribution Protocol +.Pq LDP +daemon which distribute MPLS label mappings between routers. +.Pp +A running +.Nm +can be controlled with the +.Xr ldpctl 8 +utility. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl D Ar macro Ns = Ns Ar value +Define +.Ar macro +to be set to +.Ar value +on the command line. +Overrides the definition of +.Ar macro +in the configuration file. +.It Fl d +Do not daemonize. +If this option is specified, +.Nm +will run in the foreground and log to +.Em stderr . +.It Fl f Ar file +Specify an alternative configuration file. +.It Fl n +Configtest mode. +Only check the configuration file for validity. +.It Fl v +Produce more verbose output. +.El +.Sh FILES +.Bl -tag -width "/var/run/ldpd.sockXX" -compact +.It /etc/ldpd.conf +Default +.Nm +configuration file. +.It /var/run/ldpd.sock +Unix-domain socket used for communication with +.Xr ldpctl 8 . +.El +.Sh SEE ALSO +.Xr ldpd.conf 5 , +.Xr rc.conf 8 , +.Xr ldpctl 8 +.Rs +.%R RFC 3031 +.%T Multiprotocol Label Switching Architecture +.%D January 2001 +.Re +.Rs +.%R RFC 3036 +.%T LDP Specification +.%D January 2001 +.Re +.Sh HISTORY +The +.Nm +program first appeared in +.Ox 4.6 . diff --git a/usr.sbin/ldpd/ldpd.c b/usr.sbin/ldpd/ldpd.c new file mode 100644 index 00000000000..07624cfe084 --- /dev/null +++ b/usr.sbin/ldpd/ldpd.c @@ -0,0 +1,713 @@ +/* $OpenBSD: ldpd.c,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2008 Esben Norby <norby@openbsd.org> + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/param.h> +#include <sys/sysctl.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netmpls/mpls.h> + +#include <event.h> +#include <err.h> +#include <errno.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include <unistd.h> + +#include "ldpd.h" +#include "ldp.h" +#include "ldpe.h" +#include "control.h" +#include "log.h" +#include "lde.h" + +void main_sig_handler(int, short, void *); +__dead void usage(void); +void ldpd_shutdown(void); +int check_child(pid_t, const char *); + +void main_dispatch_ldpe(int, short, void *); +void main_dispatch_lde(int, short, void *); + +/*int ldp_reload(void); */ +int ldp_sendboth(enum imsg_type, void *, u_int16_t); +/*int merge_interfaces(struct lspace *, struct lspace *); */ +/*struct iface *iface_lookup(struct lspace *, struct iface *); */ + +int pipe_parent2ldpe[2]; +int pipe_parent2lde[2]; +int pipe_ldpe2lde[2]; + +struct ldpd_conf *ldpd_conf = NULL; +struct imsgbuf *ibuf_ldpe; +struct imsgbuf *ibuf_lde; +char *conffile; + +pid_t ldpe_pid = 0; +pid_t lde_pid = 0; + +/* ARGSUSED */ +void +main_sig_handler(int sig, short event, void *arg) +{ + /* + * signal handler rules don't apply, libevent decouples for us + */ + + int die = 0; + + switch (sig) { + case SIGTERM: + case SIGINT: + die = 1; + /* FALLTHROUGH */ + case SIGCHLD: + if (check_child(ldpe_pid, "ldp engine")) { + ldpe_pid = 0; + die = 1; + } + if (check_child(lde_pid, "label decision engine")) { + lde_pid = 0; + die = 1; + } + if (die) + ldpd_shutdown(); + break; + case SIGHUP: +/* + if (ldp_reload() == -1) + log_warnx("configuration reload failed"); + else + log_debug("configuration reloaded"); +*/ + break; + default: + fatalx("unexpected signal"); + /* NOTREACHED */ + } +} + +__dead void +usage(void) +{ + extern char *__progname; + + fprintf(stderr, "usage: %s [-cdnv] [-D macro=value] [-f file]\n", + __progname); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + struct event ev_sigint, ev_sigterm, ev_sigchld, ev_sighup; + int ch, opts = 0; + int debug = 0; + int ipforwarding, mplsenable; + int mib[4]; + size_t len; + + conffile = CONF_FILE; + ldpd_process = PROC_MAIN; + + log_init(1); /* log to stderr until daemonized */ + + while ((ch = getopt(argc, argv, "cdD:f:nv")) != -1) { + switch (ch) { + case 'd': + debug = 1; + break; + case 'D': + if (cmdline_symset(optarg) < 0) + log_warnx("could not parse macro definition %s", + optarg); + break; + case 'f': + conffile = optarg; + break; + case 'n': + opts |= LDPD_OPT_NOACTION; + break; + case 'v': + if (opts & LDPD_OPT_VERBOSE) + opts |= LDPD_OPT_VERBOSE2; + opts |= LDPD_OPT_VERBOSE; + break; + + default: + usage(); + /* NOTREACHED */ + } + } + + mib[0] = CTL_NET; + mib[1] = PF_INET; + mib[2] = IPPROTO_IP; + mib[3] = IPCTL_FORWARDING; + len = sizeof(ipforwarding); + if (sysctl(mib, 4, &ipforwarding, &len, NULL, 0) == -1) + err(1, "sysctl"); + + if (ipforwarding != 1) + log_warnx("WARNING: IP forwarding NOT enabled"); + + mib[0] = CTL_NET; + mib[1] = PF_MPLS; + mib[2] = MPLSCTL_ENABLE; + len = sizeof(mplsenable); + if (sysctl(mib, 3, &mplsenable, &len, NULL, 0) == -1) + err(1, "sysctl"); + + if (mplsenable != 1) + log_warnx("WARNING: MPLS NOT enabled"); + + /* fetch interfaces early */ + kif_init(); + + /* parse config file */ + if ((ldpd_conf = parse_config(conffile, opts)) == NULL ) + exit(1); + + /* parse config file */ + + if (ldpd_conf->opts & LDPD_OPT_NOACTION) { + if (ldpd_conf->opts & LDPD_OPT_VERBOSE) + print_config(ldpd_conf); + else + fprintf(stderr, "configuration OK\n"); + exit(0); + } + + /* check for root privileges */ + if (geteuid()) + errx(1, "need root privileges"); + + /* check for ldpd user */ + if (getpwnam(LDPD_USER) == NULL) + errx(1, "unknown user %s", LDPD_USER); + + log_init(debug); + + if (!debug) + daemon(1, 0); + + log_info("startup"); + + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, + pipe_parent2ldpe) == -1) + fatal("socketpair"); + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_parent2lde) == -1) + fatal("socketpair"); + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_ldpe2lde) == -1) + fatal("socketpair"); + session_socket_blockmode(pipe_parent2ldpe[0], BM_NONBLOCK); + session_socket_blockmode(pipe_parent2ldpe[1], BM_NONBLOCK); + session_socket_blockmode(pipe_parent2lde[0], BM_NONBLOCK); + session_socket_blockmode(pipe_parent2lde[1], BM_NONBLOCK); + session_socket_blockmode(pipe_ldpe2lde[0], BM_NONBLOCK); + session_socket_blockmode(pipe_ldpe2lde[1], BM_NONBLOCK); + + /* start children */ + lde_pid = lde(ldpd_conf, pipe_parent2lde, pipe_ldpe2lde, + pipe_parent2ldpe); + ldpe_pid = ldpe(ldpd_conf, pipe_parent2ldpe, pipe_ldpe2lde, + pipe_parent2lde); + + /* show who we are */ + setproctitle("parent"); + + event_init(); + + /* setup signal handler */ + signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL); + signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL); + signal_set(&ev_sigchld, SIGCHLD, main_sig_handler, NULL); + signal_set(&ev_sighup, SIGHUP, main_sig_handler, NULL); + signal_add(&ev_sigint, NULL); + signal_add(&ev_sigterm, NULL); + signal_add(&ev_sigchld, NULL); + signal_add(&ev_sighup, NULL); + signal(SIGPIPE, SIG_IGN); + + /* setup pipes to children */ + close(pipe_parent2ldpe[1]); + close(pipe_parent2lde[1]); + close(pipe_ldpe2lde[0]); + close(pipe_ldpe2lde[1]); + + if ((ibuf_ldpe = malloc(sizeof(struct imsgbuf))) == NULL || + (ibuf_lde = malloc(sizeof(struct imsgbuf))) == NULL) + fatal(NULL); + imsg_init(ibuf_ldpe, pipe_parent2ldpe[0], main_dispatch_ldpe); + imsg_init(ibuf_lde, pipe_parent2lde[0], main_dispatch_lde); + + /* setup event handler */ + ibuf_ldpe->events = EV_READ; + event_set(&ibuf_ldpe->ev, ibuf_ldpe->fd, ibuf_ldpe->events, + ibuf_ldpe->handler, ibuf_ldpe); + event_add(&ibuf_ldpe->ev, NULL); + + ibuf_lde->events = EV_READ; + event_set(&ibuf_lde->ev, ibuf_lde->fd, ibuf_lde->events, + ibuf_lde->handler, ibuf_lde); + event_add(&ibuf_lde->ev, NULL); + + if (kr_init(!(ldpd_conf->flags & LDPD_FLAG_NO_LFIB_UPDATE)) == -1) + fatalx("kr_init failed"); + + /* remove unneded stuff from config */ + /* ... */ + + + event_dispatch(); + + ldpd_shutdown(); + /* NOTREACHED */ + return (0); +} + +void +ldpd_shutdown(void) +{ + pid_t pid; + + if (ldpe_pid) + kill(ldpe_pid, SIGTERM); + + if (lde_pid) + kill(lde_pid, SIGTERM); + + control_cleanup(); + kr_shutdown(); + + do { + if ((pid = wait(NULL)) == -1 && + errno != EINTR && errno != ECHILD) + fatal("wait"); + } while (pid != -1 || (pid == -1 && errno == EINTR)); + + msgbuf_clear(&ibuf_ldpe->w); + free(ibuf_ldpe); + msgbuf_clear(&ibuf_lde->w); + free(ibuf_lde); + free(ldpd_conf); + + close(ldpd_conf->ldp_session_socket); + + log_info("terminating"); + exit(0); +} + +int +check_child(pid_t pid, const char *pname) +{ + int status; + + if (waitpid(pid, &status, WNOHANG) > 0) { + if (WIFEXITED(status)) { + log_warnx("lost child: %s exited", pname); + return (1); + } + if (WIFSIGNALED(status)) { + log_warnx("lost child: %s terminated; signal %d", + pname, WTERMSIG(status)); + return (1); + } + } + + return (0); +} + +/* imsg handling */ +/* ARGSUSED */ +void +main_dispatch_ldpe(int fd, short event, void *bula) +{ + struct imsgbuf *ibuf = bula; + struct imsg imsg; + ssize_t n; + int shut = 0; + + if (event & EV_READ) { + if ((n = imsg_read(ibuf)) == -1) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + shut = 1; + } + if (event & EV_WRITE) { + if (msgbuf_write(&ibuf->w) == -1) + fatal("msgbuf_write"); + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("imsg_get"); + + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_CTL_RELOAD: +/* + if (ldp_reload() == -1) + log_warnx("configuration reload failed"); + else + log_debug("configuration reloaded"); + break; +*/ + case IMSG_CTL_LFIB_COUPLE: + kr_lfib_couple(); + break; + case IMSG_CTL_LFIB_DECOUPLE: + kr_lfib_decouple(); + break; + case IMSG_CTL_KROUTE: + case IMSG_CTL_KROUTE_ADDR: + kr_show_route(&imsg); + break; + case IMSG_CTL_IFINFO: + if (imsg.hdr.len == IMSG_HEADER_SIZE) + kr_ifinfo(NULL, imsg.hdr.pid); + else if (imsg.hdr.len == IMSG_HEADER_SIZE + IFNAMSIZ) + kr_ifinfo(imsg.data, imsg.hdr.pid); + else + log_warnx("IFINFO request with wrong len"); + break; + default: + log_debug("main_dispatch_ldpe: error handling imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + if (!shut) + imsg_event_add(ibuf); + else { + /* this pipe is dead, so remove the event handler */ + event_del(&ibuf->ev); + event_loopexit(NULL); + } +} + +/* ARGSUSED */ +void +main_dispatch_lde(int fd, short event, void *bula) +{ + struct imsgbuf *ibuf = bula; + struct imsg imsg; + ssize_t n; + int count, shut = 0; + + if (event & EV_READ) { + if ((n = imsg_read(ibuf)) == -1) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + shut = 1; + } + if (event & EV_WRITE) { + if (msgbuf_write(&ibuf->w) == -1) + fatal("msgbuf_write"); + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("imsg_get"); + + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_KLABEL_INSERT: + kroute_insert_label(imsg.data); + break; + case IMSG_KLABEL_CHANGE: + count = (imsg.hdr.len - IMSG_HEADER_SIZE) / + sizeof(struct kroute); + if (kr_change(imsg.data, count)) + log_warn("main_dispatch_lde: error changing " + "route"); + break; + case IMSG_KLABEL_DELETE: + if (kr_delete(imsg.data)) + log_warn("main_dispatch_lde: error deleting " + "route"); + break; + default: + log_debug("main_dispatch_lde: error handling imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + if (!shut) + imsg_event_add(ibuf); + else { + /* this pipe is dead, so remove the event handler */ + event_del(&ibuf->ev); + event_loopexit(NULL); + } +} + +void +main_imsg_compose_ldpe(int type, pid_t pid, void *data, u_int16_t datalen) +{ + imsg_compose(ibuf_ldpe, type, 0, pid, data, datalen); +} + +void +main_imsg_compose_lde(int type, pid_t pid, void *data, u_int16_t datalen) +{ + imsg_compose(ibuf_lde, type, 0, pid, data, datalen); +} + +/* this needs to be added here so that ldpctl can be used without libevent */ +void +imsg_event_add(struct imsgbuf *ibuf) +{ + ibuf->events = EV_READ; + if (ibuf->w.queued) + ibuf->events |= EV_WRITE; + + event_del(&ibuf->ev); + event_set(&ibuf->ev, ibuf->fd, ibuf->events, ibuf->handler, ibuf); + event_add(&ibuf->ev, NULL); +} + +/* +int +ldp_reload(void) +{ + struct lspace *lspace; + struct iface *iface; + struct ldpd_conf *xconf; + + if ((xconf = parse_config(conffile, ldpd_conf->opts)) == NULL) + return (-1); + + if (ldp_sendboth(IMSG_RECONF_CONF, xconf, sizeof(*xconf)) == -1) + return (-1); + + LIST_FOREACH(lspace, &xconf->lspace_list, entry) { + if (ldp_sendboth(IMSG_RECONF_AREA, lspace, sizeof(*lspace)) == -1) + return (-1); + + LIST_FOREACH(iface, &lspace->iface_list, entry) { + if (ldp_sendboth(IMSG_RECONF_IFACE, iface, + sizeof(*iface)) == -1) + return (-1); + } + } + + if (ldp_sendboth(IMSG_RECONF_END, NULL, 0) == -1) + return (-1); + + merge_config(ldpd_conf, xconf); + + kr_reload(); + return (0); +} + +int +ldp_sendboth(enum imsg_type type, void *buf, u_int16_t len) +{ + if (imsg_compose(ibuf_ldpe, type, 0, 0, buf, len) == -1) + return (-1); + if (imsg_compose(ibuf_lde, type, 0, 0, buf, len) == -1) + return (-1); + return (0); +} + +void +merge_config(struct ldpd_conf *conf, struct ldpd_conf *xconf) +{ + struct lspace *a, *xa, *na; + struct iface *iface; + struct redistribute *r; + int rchange = 0; + + conf->flags = xconf->flags; + conf->spf_delay = xconf->spf_delay; + conf->spf_hold_time = xconf->spf_hold_time; + if ((conf->redistribute & REDISTRIBUTE_ON) != + (xconf->redistribute & REDISTRIBUTE_ON)) + rchange = 1; + conf->redistribute = xconf->redistribute; + conf->rfc1583compat = xconf->rfc1583compat; + + if (ldpd_process == PROC_MAIN) { + while ((r = SIMPLEQ_FIRST(&conf->redist_list)) != NULL) { + SIMPLEQ_REMOVE_HEAD(&conf->redist_list, entry); + free(r); + } + while ((r = SIMPLEQ_FIRST(&xconf->redist_list)) != NULL) { + SIMPLEQ_REMOVE_HEAD(&xconf->redist_list, entry); + SIMPLEQ_INSERT_TAIL(&conf->redist_list, r, entry); + } + goto done; + } + + for (a = LIST_FIRST(&conf->lspace_list); a != NULL; a = na) { + na = LIST_NEXT(a, entry); + if ((xa = lspace_find(xconf, a->id)) == NULL) { + if (ldpd_process == PROC_LDP_ENGINE) { + LIST_FOREACH(iface, &a->iface_list, entry) + if_fsm(iface, IF_EVT_DOWN); + } + LIST_REMOVE(a, entry); + lspace_del(a); + } + } + + for (xa = LIST_FIRST(&xconf->lspace_list); xa != NULL; xa = na) { + na = LIST_NEXT(xa, entry); + if ((a = lspace_find(conf, xa->id)) == NULL) { + LIST_REMOVE(xa, entry); + LIST_INSERT_HEAD(&conf->lspace_list, xa, entry); + if (ldpd_process == PROC_LDP_ENGINE) { + LIST_FOREACH(iface, &xa->iface_list, entry) { + if_init(conf, iface); + if (if_fsm(iface, IF_EVT_UP)) { + log_debug("error starting " + "interface %s", + iface->name); + } + } + } + continue; + } + a->stub = xa->stub; + a->stub_default_cost = xa->stub_default_cost; + if (ldpd_process == PROC_LDE_ENGINE) + a->dirty = 1; + + if (merge_interfaces(a, xa) && + ldpd_process == PROC_LDP_ENGINE) + a->dirty = 1; + } + + if (ldpd_process == PROC_LDP_ENGINE) { + LIST_FOREACH(a, &conf->lspace_list, entry) { + LIST_FOREACH(iface, &a->iface_list, entry) { + if (iface->state == IF_STA_NEW) { + iface->state = IF_STA_DOWN; + if_init(conf, iface); + if (if_fsm(iface, IF_EVT_UP)) { + log_debug("error starting " + "interface %s", + iface->name); + } + } + } + } + } + +done: + while ((a = LIST_FIRST(&xconf->lspace_list)) != NULL) { + LIST_REMOVE(a, entry); + lspace_del(a); + } + free(xconf); +} + +int +merge_interfaces(struct lspace *a, struct lspace *xa) +{ + struct iface *i, *xi, *ni; + int dirty = 0; + + for (i = LIST_FIRST(&a->iface_list); i != NULL; i = ni) { + ni = LIST_NEXT(i, entry); + if (iface_lookup(xa, i) == NULL) { + log_debug("merge_config: proc %d label space %s removing " + "interface %s", ldpd_process, inet_ntoa(a->id), + i->name); + if (ldpd_process == PROC_LDP_ENGINE) + if_fsm(i, IF_EVT_DOWN); + LIST_REMOVE(i, entry); + if_del(i); + } + } + + for (xi = LIST_FIRST(&xa->iface_list); xi != NULL; xi = ni) { + ni = LIST_NEXT(xi, entry); + if ((i = iface_lookup(a, xi)) == NULL) { + log_debug("merge_config: proc %d label space %s adding " + "interface %s", ldpd_process, inet_ntoa(a->id), + xi->name); + LIST_REMOVE(xi, entry); + LIST_INSERT_HEAD(&a->iface_list, xi, entry); + xi->lspace = a; + if (ldpd_process == PROC_LDP_ENGINE) + xi->state = IF_STA_NEW; + continue; + } + log_debug("merge_config: proc %d label space %s merging interface %s", + ldpd_process, inet_ntoa(a->id), i->name); + i->addr = xi->addr; + i->dst = xi->dst; + i->mask = xi->mask; + i->abr_id = xi->abr_id; + i->baudrate = xi->baudrate; + i->dead_interval = xi->dead_interval; + i->mtu = xi->mtu; + i->transmit_delay = xi->transmit_delay; + i->hello_interval = xi->hello_interval; + i->rxmt_interval = xi->rxmt_interval; + if (i->metric != xi->metric) + dirty = 1; + i->metric = xi->metric; + i->priority = xi->priority; + i->flags = xi->flags; + i->type = xi->type; + i->media_type = xi->media_type; + i->linkstate = xi->linkstate; + + if (i->passive != xi->passive) { + if (ldpd_process == PROC_LDP_ENGINE) + if_fsm(i, IF_EVT_DOWN); + i->passive = xi->passive; + if (ldpd_process == PROC_LDP_ENGINE) + if_fsm(i, IF_EVT_UP); + } + } + return (dirty); +} + +struct iface * +iface_lookup(struct lspace *lspace, struct iface *iface) +{ + struct iface *i; + + LIST_FOREACH(i, &lspace->iface_list, entry) + if (i->ifindex == iface->ifindex) + return (i); + return (NULL); +} +*/ diff --git a/usr.sbin/ldpd/ldpd.conf.5 b/usr.sbin/ldpd/ldpd.conf.5 new file mode 100644 index 00000000000..c2e1ed425ea --- /dev/null +++ b/usr.sbin/ldpd/ldpd.conf.5 @@ -0,0 +1,225 @@ +.\" $OpenBSD: ldpd.conf.5,v 1.1 2009/06/01 20:59:45 michele Exp $ +.\" +.\" Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> +.\" Copyright (c) 2005, 2006 Esben Norby <norby@openbsd.org> +.\" Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org> +.\" Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> +.\" Copyright (c) 2002 Daniel Hartmeier <dhartmei@openbsd.org> +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: June 1 2009 $ +.Dt LDPD.CONF 5 +.Os +.Sh NAME +.Nm ldpd.conf +.Nd Label Distribution Protocol daemon configuration file +.Sh DESCRIPTION +The +.Xr ldpd 8 +daemon implements the Label Distribution Protocol as described in RFC 3036. +.Sh SECTIONS +The +.Nm +config file is divided into three main sections. +.Bl -tag -width xxxx +.It Sy Macros +User-defined variables may be defined and used later, simplifying the +configuration file. +.It Sy Global Configuration +Global settings for +.Xr ldpd 8 . +.It Sy Interfaces Configuration +Interface-specific parameters. +.El +.Sh MACROS +Much like +.Xr cpp 1 +or +.Xr m4 1 , +macros can be defined that will later be expanded in context. +Macro names must start with a letter, and may contain letters, digits, +and underscores. +Macro names may not be reserved words (for example, +.Ic cost ) . +Macros are not expanded inside quotes. +.Pp +For example: +.Bd -literal -offset indent +hi="2" +interface em0 { + cost $hi +} +.Ed +.Sh GLOBAL CONFIGURATION +Global settings concerns the main behaviour of the daemon. +.Pp +.Bl -tag -width Ds -compact +.It Xo +.Ic fib-update +.Pq Ic yes Ns \&| Ns Ic no +.Xc +If set to +.Ic no , +do not update the Forwarding Information Base, a.k.a. the kernel routing +table. +The default is +.Ic yes . +.Pp +.It Xo +.Op Ic no +.Ic redistribute +.Sm off +.Po Ic static Ns \&| Ns Ic connected Ns \&| Ns +.Ic default Pc +.Sm on +.Xc +.It Xo +.Op Ic no +.Ic redistribute Ar prefix +.Xc +.It Xo +.Op Ic no +.Ic redistribute rtlabel Ar label +.Xc +If set to +.Ic connected , +routes to directly attached networks will be +announced over RIP. +If set to +.Ic static , +static routes will be announced over RIP. +If set to +.Ic default , +a default route pointing to this router will be announced over RIP. +It is possible to specify a network range with +.Ar prefix ; +networks need to be part of that range to be redistributed. +Additionally it is possible to redistribute based on route labels +using the +.Ic rtlabel +keyword. +By default no additional routes will be announced over RIP. +.Pp +.Ic redistribute +statements are evaluated in sequential order, from first to last. +The first matching rule decides if a route should be redistributed or not. +Matching rules starting with +.Ic no +will force the route to be not announced. +.Pp +.It Xo +.Ic split-horizon +.Sm off +.Po Ic default Ns \&| Ns Ic poisoned Ns \&| Ns +.Ic none Pc +.Sm on +.Xc +If set to +.Ic default , +do not redistribute routes to the interface from which they were learned. +If set to +.Ic poisoned , +redistribute routes to the interface from which they were learned, by +forcing the metric to infinity. +Both these behaviours can resolve routing loops when a router goes down. +The default is +.Ic none . +.Pp +.It Xo +.Ic triggered-updates +.Pq Ic yes Ns \&| Ns Ic no +.Xc +Immediately trigger responses when a route changes. +The default is +.Ic yes . +.El +.Sh INTERFACES +Each interface can have several parameters configured individually, otherwise +they are inherited. +.Bd -literal -offset indent +interface em0 { +} +.Ed +.Pp +Interface-specific parameters are listed below. +.Bl -tag -width Ds +.It Ic auth-key Ar key +Set the authentication key for +.Ic simple +authentication. +Up to 16 characters can be specified. +.It Ic auth-md Ar key-id key +Set the authentication +.Ar key-id +and +.Ar key +for +.Ic crypt +authentication. +The valid range for +.Ar key-id +is 0\-255. +Up to 16 characters can be specified for +.Ar key . +Multiple keys may be specified. +.It Ic auth-md-keyid Ar key-id +Configure the +.Ar key-id +to use for +.Ic crypt +authentication. +The valid range for +.Ar key-id +is 0\-255. +The default key-id is 1. +.It Xo +.Ic auth-type +.Po Ic none Ns \&| Ns +.Ic simple Ns \&| Ns Ic crypt Pc +.Xc +Set the authentication type. +The default is +.Ic none . +Simple authentication uses a plaintext password, up to 16 characters. +Crypt authentication uses an MD5 hash. +.It Ic cost Ar number +Cost of the network connected through this interface. +Values must be between 1 and 16, inclusive. +.It Ic demote Ar group +Increase the +.Xr carp 4 +demotion counter by 1 on the given interface group, usually +.Ar carp , +when the interface state is going down. +The demotion counter will be decreased when the interface +state is active again. +.It Ic passive +Prevent transmission and reception of LDP packets on this interface. +.El +.Sh FILES +.Bl -tag -width "/etc/ldpd.conf" -compact +.It Pa /etc/ldpd.conf +.Xr ldpd 8 +configuration file +.El +.Sh SEE ALSO +.Xr rc.conf.local 8 , +.Xr ldpctl 8 , +.Xr ldpd 8 +.Rs +.Sh HISTORY +The +.Nm +file format first appeared in +.Ox 4.6 . diff --git a/usr.sbin/ldpd/ldpd.h b/usr.sbin/ldpd/ldpd.h new file mode 100644 index 00000000000..3e76e53c1fb --- /dev/null +++ b/usr.sbin/ldpd/ldpd.h @@ -0,0 +1,494 @@ +/* $OpenBSD: ldpd.h,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * Copyright (c) 2004 Esben Norby <norby@openbsd.org> + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LDPD_H_ +#define _LDPD_H_ + +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/tree.h> +#include <md5.h> +#include <net/if.h> +#include <netinet/in.h> +#include <event.h> + +#include "ldp.h" + +#define CONF_FILE "/etc/ldpd.conf" +#define LDPD_SOCKET "/var/run/ldpd.sock" +#define LDPD_USER "_ldpd" + +#define NBR_HASHSIZE 128 + +#define NBR_IDSELF 1 +#define NBR_CNTSTART (NBR_IDSELF + 1) + +#define READ_BUF_SIZE 65535 +#define RT_BUF_SIZE 16384 +#define MAX_RTSOCK_BUF 128 * 1024 +#define LDP_BACKLOG 128 + +#define LDPD_FLAG_NO_LFIB_UPDATE 0x0001 + +#define F_LDPD_INSERTED 0x0001 +#define F_KERNEL 0x0002 +#define F_BGPD_INSERTED 0x0004 +#define F_CONNECTED 0x0008 +#define F_DOWN 0x0010 +#define F_STATIC 0x0020 +#define F_DYNAMIC 0x0040 +#define F_REDISTRIBUTED 0x0100 + +/* buffer */ +struct buf { + TAILQ_ENTRY(buf) entry; + u_char *buf; + size_t size; + size_t max; + size_t wpos; + size_t rpos; +}; + +struct msgbuf { + TAILQ_HEAD(, buf) bufs; + u_int32_t queued; + int fd; +}; + +#define IMSG_HEADER_SIZE sizeof(struct imsg_hdr) +#define MAX_IMSGSIZE 8192 + +struct buf_read { + u_char buf[READ_BUF_SIZE]; + u_char *rptr; + size_t wpos; +}; + +struct imsgbuf { + TAILQ_HEAD(, imsg_fd) fds; + struct buf_read r; + struct msgbuf w; + struct event ev; + void (*handler)(int, short, void *); + int fd; + pid_t pid; + short events; +}; + +enum imsg_type { + IMSG_NONE, + IMSG_CTL_RELOAD, + IMSG_CTL_SHOW_INTERFACE, + IMSG_CTL_SHOW_NBR, + IMSG_CTL_SHOW_LIB, + IMSG_CTL_LFIB_COUPLE, + IMSG_CTL_LFIB_DECOUPLE, + IMSG_CTL_KROUTE, + IMSG_CTL_KROUTE_ADDR, + IMSG_CTL_IFINFO, + IMSG_CTL_END, + IMSG_KLABEL_INSERT, + IMSG_KLABEL_CHANGE, + IMSG_KLABEL_DELETE, + IMSG_KROUTE_GET, + IMSG_IFINFO, + IMSG_LABEL_MAPPING, + IMSG_LABEL_MAPPING_FULL, + IMSG_LABEL_REQUEST, + IMSG_REQUEST_ADD, + IMSG_REQUEST_ADD_END, + IMSG_MAPPING_ADD, + IMSG_MAPPING_ADD_END, + IMSG_RELEASE_ADD, + IMSG_RELEASE_ADD_END, + IMSG_ADDRESS_ADD, + IMSG_ADDRESS_DEL, + IMSG_NOTIFICATION_SEND, + IMSG_NEIGHBOR_UP, + IMSG_NEIGHBOR_DOWN, + IMSG_NEIGHBOR_CHANGE, + IMSG_NETWORK_ADD, + IMSG_NETWORK_DEL, + IMSG_RECONF_CONF, + IMSG_RECONF_AREA, + IMSG_RECONF_IFACE, + IMSG_RECONF_END +}; + +struct imsg_hdr { + enum imsg_type type; + u_int16_t len; + u_int32_t peerid; + pid_t pid; +}; + +struct imsg { + struct imsg_hdr hdr; + void *data; +}; + +/* interface states */ +#define IF_STA_NEW 0x00 /* dummy state for reload */ +#define IF_STA_DOWN 0x01 +#define IF_STA_LOOPBACK 0x02 +#define IF_STA_POINTTOPOINT 0x04 +#define IF_STA_DROTHER 0x08 +#define IF_STA_MULTI (IF_STA_DROTHER | IF_STA_BACKUP | IF_STA_DR) +#define IF_STA_ANY 0x7f +#define IF_STA_ACTIVE (~IF_STA_DOWN) + +/* interface events */ +enum iface_event { + IF_EVT_NOTHING, + IF_EVT_UP, + IF_EVT_DOWN +}; + +/* interface actions */ +enum iface_action { + IF_ACT_NOTHING, + IF_ACT_STRT, + IF_ACT_RST +}; + +/* interface types */ +enum iface_type { + IF_TYPE_POINTOPOINT, + IF_TYPE_BROADCAST, + IF_TYPE_NBMA, + IF_TYPE_POINTOMULTIPOINT, + IF_TYPE_VIRTUALLINK +}; + +/* neighbor states */ +#define NBR_STA_DOWN 0x0001 +#define NBR_STA_PRESENT 0x0002 +#define NBR_STA_INITIAL 0x0004 +#define NBR_STA_OPENREC 0x0008 +#define NBR_STA_OPENSENT 0x0010 +#define NBR_STA_OPER 0x0020 +#define NBR_STA_ACTIVE (~NBR_STA_DOWN) +#define NBR_STA_SESSION (NBR_STA_PRESENT | NBR_STA_PRESENT | \ + NBR_STA_INITIAL | NBR_STA_OPENREC | \ + NBR_STA_OPER | NBR_STA_OPENSENT | \ + NBR_STA_ACTIVE) +#define NBR_STA_UP (NBR_STA_PRESENT | NBR_STA_SESSION) + +/* neighbor events */ +enum nbr_event { + NBR_EVT_NOTHING, + NBR_EVT_HELLO_RCVD, + NBR_EVT_SESSION_UP, + NBR_EVT_CLOSE_SESSION, + NBR_EVT_INIT_RCVD, + NBR_EVT_KEEPALIVE_RCVD, + NBR_EVT_PDU_RCVD, + NBR_EVT_INIT_SENT, + NBR_EVT_DOWN +}; + +/* neighbor actions */ +enum nbr_action { + NBR_ACT_NOTHING, + NBR_ACT_STRT_ITIMER, + NBR_ACT_RST_ITIMER, + NBR_ACT_RST_KTIMEOUT, + NBR_ACT_STRT_KTIMER, + NBR_ACT_RST_KTIMER, + NBR_ACT_SESSION_EST, + NBR_ACT_INIT_SEND, + NBR_ACT_KEEPALIVE_SEND, + NBR_ACT_CLOSE_SESSION +}; + +TAILQ_HEAD(mapping_head, mapping_entry); + +struct map { + u_int32_t label; + u_int32_t prefix; + u_int8_t prefixlen; + u_int32_t messageid; +}; + +struct iface { + LIST_ENTRY(iface) entry; + struct event hello_timer; + + LIST_HEAD(, nbr) nbr_list; + LIST_HEAD(, lde_nbr) lde_nbr_list; + + char name[IF_NAMESIZE]; + struct in_addr addr; + struct in_addr dst; + struct in_addr mask; + struct nbr *self; + + u_int16_t lspace_id; + + u_int64_t baudrate; + time_t uptime; + unsigned int ifindex; + int discovery_fd; + int session_fd; + int state; + int mtu; + u_int16_t holdtime; + u_int16_t keepalive; + u_int16_t hello_interval; + u_int16_t flags; + enum iface_type type; + u_int8_t media_type; + u_int8_t linkstate; + u_int8_t priority; + u_int8_t passive; +}; + +/* ldp_conf */ +enum { + PROC_MAIN, + PROC_LDP_ENGINE, + PROC_LDE_ENGINE +} ldpd_process; + +#define MODE_DIST_INDEPENDENT 0x01 +#define MODE_DIST_ORDERED 0x02 +#define MODE_RET_LIBERAL 0x04 +#define MODE_RET_CONSERVATIVE 0x08 +#define MODE_ADV_ONDEMAND 0x10 +#define MODE_ADV_UNSOLICITED 0x20 + +struct ldpd_conf { + struct event disc_ev, sess_ev; + struct in_addr rtr_id; + LIST_HEAD(, iface) iface_list; + + u_int32_t opts; +#define LDPD_OPT_VERBOSE 0x00000001 +#define LDPD_OPT_VERBOSE2 0x00000002 +#define LDPD_OPT_NOACTION 0x00000004 + time_t uptime; + int ldp_discovery_socket; + int ldp_session_socket; + int flags; + u_int8_t mode; +}; + +/* kroute */ +struct kroute { + struct in_addr prefix; + struct in_addr nexthop; + u_int32_t local_label; + u_int32_t remote_label; + u_int16_t flags; + u_int16_t rtlabel; + u_int32_t ext_tag; + u_short ifindex; + u_int8_t prefixlen; +}; + +struct rroute { + struct kroute kr; + u_int32_t metric; +}; + +struct kif_addr { + TAILQ_ENTRY(kif_addr) entry; + struct in_addr addr; + struct in_addr mask; + struct in_addr dstbrd; +}; + +struct kif { + char ifname[IF_NAMESIZE]; + u_int64_t baudrate; + int flags; + int mtu; + u_short ifindex; + u_int8_t media_type; + u_int8_t link_state; + u_int8_t nh_reachable; /* for nexthop verification */ +}; + +/* name2id */ +struct n2id_label { + TAILQ_ENTRY(n2id_label) entry; + char *name; + u_int16_t id; + u_int32_t ext_tag; + int ref; +}; + +TAILQ_HEAD(n2id_labels, n2id_label); +extern struct n2id_labels rt_labels; + +/* control data structures */ +struct ctl_iface { + char name[IF_NAMESIZE]; + struct in_addr addr; + struct in_addr mask; + struct in_addr lspace; + struct in_addr rtr_id; + struct in_addr dr_id; + struct in_addr dr_addr; + struct in_addr bdr_id; + struct in_addr bdr_addr; + time_t hello_timer; + time_t uptime; + u_int64_t baudrate; + unsigned int ifindex; + int state; + int mtu; + int nbr_cnt; + int adj_cnt; + u_int16_t flags; + u_int16_t holdtime; + u_int16_t hello_interval; + enum iface_type type; + u_int8_t linkstate; + u_int8_t mediatype; + u_int8_t priority; + u_int8_t passive; +}; + +struct ctl_nbr { + char name[IF_NAMESIZE]; + struct in_addr id; + struct in_addr addr; + struct in_addr dr; + struct in_addr bdr; + struct in_addr lspace; + time_t dead_timer; + time_t uptime; + u_int32_t db_sum_lst_cnt; + u_int32_t ls_req_lst_cnt; + u_int32_t ls_retrans_lst_cnt; + u_int32_t state_chng_cnt; + int nbr_state; + int iface_state; + u_int8_t priority; + u_int8_t options; +}; + +struct ctl_rt { + struct in_addr prefix; + struct in_addr nexthop; + struct in_addr lspace; + struct in_addr adv_rtr; + time_t uptime; + u_int8_t flags; + u_int8_t prefixlen; + u_int32_t local_label; + u_int32_t remote_label; +}; + +struct ctl_sum { + struct in_addr rtr_id; + u_int32_t spf_delay; + u_int32_t spf_hold_time; + u_int32_t num_ext_lsa; + u_int32_t num_lspace; + time_t uptime; + u_int8_t rfc1583compat; +}; + +struct ctl_sum_lspace { + struct in_addr lspace; + u_int32_t num_iface; + u_int32_t num_adj_nbr; + u_int32_t num_spf_calc; + u_int32_t num_lsa; +}; + +/* buffer.c */ +struct buf *buf_open(size_t); +struct buf *buf_dynamic(size_t, size_t); +int buf_add(struct buf *, void *, size_t); +void *buf_reserve(struct buf *, size_t); +void *buf_seek(struct buf *, size_t, size_t); +int buf_close(struct msgbuf *, struct buf *); +void buf_free(struct buf *); +void msgbuf_init(struct msgbuf *); +void msgbuf_clear(struct msgbuf *); +int msgbuf_write(struct msgbuf *); + +/* parse.y */ +struct ldpd_conf *parse_config(char *, int); +int cmdline_symset(char *); + +/* imsg.c */ +void imsg_init(struct imsgbuf *, int, void (*)(int, short, void *)); +ssize_t imsg_read(struct imsgbuf *); +ssize_t imsg_get(struct imsgbuf *, struct imsg *); +int imsg_compose(struct imsgbuf *, enum imsg_type, u_int32_t, pid_t, + void *, u_int16_t); +struct buf *imsg_create(struct imsgbuf *, enum imsg_type, u_int32_t, pid_t, + u_int16_t); +int imsg_add(struct buf *, void *, u_int16_t); +int imsg_close(struct imsgbuf *, struct buf *); +void imsg_free(struct imsg *); +void imsg_event_add(struct imsgbuf *); /* needs to be provided externally */ + +/* in_cksum.c */ +u_int16_t in_cksum(void *, size_t); + +/* iso_cksum.c */ +u_int16_t iso_cksum(void *, u_int16_t, u_int16_t); + +/* kroute.c */ +int kif_init(void); +int kr_init(int); +int kr_change(struct kroute *, int); +int kr_delete(struct kroute *); +void kr_shutdown(void); +void kr_lfib_couple(void); +void kr_lfib_decouple(void); +void kr_dispatch_msg(int, short, void *); +void kr_show_route(struct imsg *); +void kr_ifinfo(char *, pid_t); +struct kif *kif_findname(char *, struct in_addr, struct kif_addr **); +void kr_reload(void); +int kroute_insert_label(struct kroute *); + +u_int8_t mask2prefixlen(in_addr_t); +in_addr_t prefixlen2mask(u_int8_t); + +/* log.h */ +const char *nbr_state_name(int); +const char *if_state_name(int); +const char *if_type_name(enum iface_type); + +/* name2id.c */ +u_int16_t rtlabel_name2id(const char *); +const char *rtlabel_id2name(u_int16_t); +void rtlabel_unref(u_int16_t); +u_int32_t rtlabel_id2tag(u_int16_t); +u_int16_t rtlabel_tag2id(u_int32_t); +void rtlabel_tag(u_int16_t, u_int32_t); + +/* ldpd.c */ +void main_imsg_compose_ldpe(int, pid_t, void *, u_int16_t); +void main_imsg_compose_lde(int, pid_t, void *, u_int16_t); +void merge_config(struct ldpd_conf *, struct ldpd_conf *); + +/* printconf.c */ +void print_config(struct ldpd_conf *); + +#endif /* _LDPD_H_ */ diff --git a/usr.sbin/ldpd/ldpe.c b/usr.sbin/ldpd/ldpe.c new file mode 100644 index 00000000000..3e1dddf19a5 --- /dev/null +++ b/usr.sbin/ldpd/ldpe.c @@ -0,0 +1,544 @@ +/* $OpenBSD: ldpe.c,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2008 Esben Norby <norby@openbsd.org> + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <net/if_types.h> +#include <stdlib.h> +#include <signal.h> +#include <string.h> +#include <fcntl.h> +#include <pwd.h> +#include <unistd.h> +#include <event.h> +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> + +#include "ldp.h" +#include "ldpd.h" +#include "ldpe.h" +#include "lde.h" +#include "control.h" +#include "log.h" + +void ldpe_sig_handler(int, short, void *); +void ldpe_shutdown(void); + +void recv_packet(int, short, void *); + +struct ldpd_conf *leconf = NULL, *nconf; +struct imsgbuf *ibuf_main; +struct imsgbuf *ibuf_lde; +int oe_nofib; + +/* ARGSUSED */ +void +ldpe_sig_handler(int sig, short event, void *bula) +{ + switch (sig) { + case SIGINT: + case SIGTERM: + ldpe_shutdown(); + /* NOTREACHED */ + default: + fatalx("unexpected signal"); + } +} + +/* label distribution protocol engine */ +pid_t +ldpe(struct ldpd_conf *xconf, int pipe_parent2ldpe[2], int pipe_ldpe2lde[2], + int pipe_parent2lde[2]) +{ + struct iface *iface; + struct passwd *pw; + struct event ev_sigint, ev_sigterm; + struct sockaddr_in disc_addr, sess_addr; + pid_t pid; + + switch (pid = fork()) { + case -1: + fatal("cannot fork"); + case 0: + break; + default: + return (pid); + } + + /* create ldpd control socket outside chroot */ + if (control_init() == -1) + fatalx("control socket setup failed"); + + /* create the discovery UDP socket */ + disc_addr.sin_family = AF_INET; + disc_addr.sin_port = htons(LDP_PORT); + disc_addr.sin_addr.s_addr = INADDR_ANY; + + if ((xconf->ldp_discovery_socket = socket(AF_INET, SOCK_DGRAM, + IPPROTO_UDP)) == -1) + fatal("error creating discovery socket"); + + if (bind(xconf->ldp_discovery_socket, (struct sockaddr *)&disc_addr, + sizeof(disc_addr)) == -1) + fatal("error binding discovery socket"); + + /* set some defaults */ + if (if_set_mcast_ttl(xconf->ldp_discovery_socket, + IP_DEFAULT_MULTICAST_TTL) == -1) + fatal("if_set_mcast_ttl"); + if (if_set_mcast_loop(xconf->ldp_discovery_socket) == -1) + fatal("if_set_mcast_loop"); + if (if_set_tos(xconf->ldp_discovery_socket, + IPTOS_PREC_INTERNETCONTROL) == -1) + fatal("if_set_tos"); + if (if_set_recvif(xconf->ldp_discovery_socket, 1) == -1) + fatal("if_set_recvif"); + if_set_recvbuf(xconf->ldp_discovery_socket); + + /* create the session TCP socket */ + sess_addr.sin_family = AF_INET; + sess_addr.sin_port = htons(LDP_PORT); + sess_addr.sin_addr.s_addr = INADDR_ANY; + + if ((xconf->ldp_session_socket = socket(AF_INET, SOCK_STREAM, + IPPROTO_TCP)) == -1) + fatal("error creating session socket"); + + if (bind(xconf->ldp_session_socket, (struct sockaddr *)&sess_addr, + sizeof(sess_addr)) == -1) + fatal("error binding session socket"); + + if (listen(xconf->ldp_session_socket, LDP_BACKLOG) == -1) + fatal("error in listen on session socket"); + + /* set some defaults */ + if (if_set_tos(xconf->ldp_session_socket, + IPTOS_PREC_INTERNETCONTROL) == -1) + fatal("if_set_tos"); + if (if_set_reuse(xconf->ldp_session_socket, 1) == -1) + fatal("if_set_reuse"); + if (if_set_nonblock(xconf->ldp_session_socket) == -1) + fatal("if_set_nonblock"); + + leconf = xconf; + if (leconf->flags & LDPD_FLAG_NO_LFIB_UPDATE) + oe_nofib = 1; + + if ((pw = getpwnam(LDPD_USER)) == NULL) + fatal("getpwnam"); + + if (chroot(pw->pw_dir) == -1) + fatal("chroot"); + if (chdir("/") == -1) + fatal("chdir(\"/\")"); + + setproctitle("ldp engine"); + ldpd_process = PROC_LDP_ENGINE; + + if (setgroups(1, &pw->pw_gid) || + setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || + setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) + fatal("can't drop privileges"); + + event_init(); + nbr_init(NBR_HASHSIZE); + + /* setup signal handler */ + signal_set(&ev_sigint, SIGINT, ldpe_sig_handler, NULL); + signal_set(&ev_sigterm, SIGTERM, ldpe_sig_handler, NULL); + signal_add(&ev_sigint, NULL); + signal_add(&ev_sigterm, NULL); + signal(SIGPIPE, SIG_IGN); + signal(SIGHUP, SIG_IGN); + + /* setup pipes */ + close(pipe_parent2ldpe[0]); + close(pipe_ldpe2lde[1]); + close(pipe_parent2lde[0]); + close(pipe_parent2lde[1]); + + if ((ibuf_lde = malloc(sizeof(struct imsgbuf))) == NULL || + (ibuf_main = malloc(sizeof(struct imsgbuf))) == NULL) + fatal(NULL); + imsg_init(ibuf_lde, pipe_ldpe2lde[0], ldpe_dispatch_lde); + imsg_init(ibuf_main, pipe_parent2ldpe[1], ldpe_dispatch_main); + + /* setup event handler */ + ibuf_lde->events = EV_READ; + event_set(&ibuf_lde->ev, ibuf_lde->fd, ibuf_lde->events, + ibuf_lde->handler, ibuf_lde); + event_add(&ibuf_lde->ev, NULL); + + ibuf_main->events = EV_READ; + event_set(&ibuf_main->ev, ibuf_main->fd, ibuf_main->events, + ibuf_main->handler, ibuf_main); + event_add(&ibuf_main->ev, NULL); + + event_set(&leconf->disc_ev, leconf->ldp_discovery_socket, + EV_READ|EV_PERSIST, disc_recv_packet, leconf); + event_add(&leconf->disc_ev, NULL); + + event_set(&leconf->sess_ev, leconf->ldp_session_socket, + EV_READ|EV_PERSIST, session_recv_packet, leconf); + event_add(&leconf->sess_ev, NULL); + + /* listen on ldpd control socket */ + TAILQ_INIT(&ctl_conns); + control_listen(); + + if ((pkt_ptr = calloc(1, READ_BUF_SIZE)) == NULL) + fatal("ldpe"); + + /* start interfaces */ + LIST_FOREACH(iface, &leconf->iface_list, entry) { + if_init(xconf, iface); + if (if_fsm(iface, IF_EVT_UP)) { + log_debug("error starting interface %s", + iface->name); + } + } + + event_dispatch(); + + ldpe_shutdown(); + /* NOTREACHED */ + return (0); +} + +void +ldpe_shutdown(void) +{ + struct iface *iface; + + /* stop all interfaces */ + LIST_FOREACH(iface, &leconf->iface_list, entry) { + if (if_fsm(iface, IF_EVT_DOWN)) { + log_debug("error stopping interface %s", + iface->name); + } + } + + close(leconf->ldp_discovery_socket); + + /* clean up */ + msgbuf_write(&ibuf_lde->w); + msgbuf_clear(&ibuf_lde->w); + free(ibuf_lde); + msgbuf_write(&ibuf_main->w); + msgbuf_clear(&ibuf_main->w); + free(ibuf_main); + free(leconf); + free(pkt_ptr); + + log_info("ldp engine exiting"); + _exit(0); +} + +/* imesg */ +int +ldpe_imsg_compose_parent(int type, pid_t pid, void *data, u_int16_t datalen) +{ + return (imsg_compose(ibuf_main, type, 0, pid, data, datalen)); +} + +int +ldpe_imsg_compose_lde(int type, u_int32_t peerid, pid_t pid, + void *data, u_int16_t datalen) +{ + return (imsg_compose(ibuf_lde, type, peerid, pid, data, datalen)); +} + +/* ARGSUSED */ +void +ldpe_dispatch_main(int fd, short event, void *bula) +{ + struct imsg imsg; + struct imsgbuf *ibuf = bula; + struct iface *iface = NULL; + struct kif *kif; + int n, link_ok, shut = 0; + + if (event & EV_READ) { + if ((n = imsg_read(ibuf)) == -1) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + shut = 1; + } + if (event & EV_WRITE) { + if (msgbuf_write(&ibuf->w) == -1) + fatal("msgbuf_write"); + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("ldpe_dispatch_main: imsg_read error"); + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_IFINFO: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(struct kif)) + fatalx("IFINFO imsg with wrong len"); + kif = imsg.data; + link_ok = (kif->flags & IFF_UP) && + (LINK_STATE_IS_UP(kif->link_state) || + (kif->link_state == LINK_STATE_UNKNOWN && + kif->media_type != IFT_CARP)); + + LIST_FOREACH(iface, &leconf->iface_list, entry) { + if (kif->ifindex == iface->ifindex && + iface->type != IF_TYPE_VIRTUALLINK) { + iface->flags = kif->flags; + iface->linkstate = kif->link_state; + + if (link_ok) { + if_fsm(iface, IF_EVT_UP); + log_warnx("interface %s up", + iface->name); + /* XXX: send address msg */ + } else { + if_fsm(iface, IF_EVT_DOWN); + log_warnx("interface %s down", + iface->name); + /* XXX: send address withdraw + msg */ + } + } + } + break; + case IMSG_RECONF_CONF: + break; + case IMSG_RECONF_AREA: + break; + case IMSG_RECONF_IFACE: + break; + case IMSG_RECONF_END: + break; + case IMSG_CTL_KROUTE: + case IMSG_CTL_KROUTE_ADDR: + case IMSG_CTL_IFINFO: + case IMSG_CTL_END: + control_imsg_relay(&imsg); + break; + default: + log_debug("ldpe_dispatch_main: error handling imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + if (!shut) + imsg_event_add(ibuf); + else { + /* this pipe is dead, so remove the event handler */ + event_del(&ibuf->ev); + event_loopexit(NULL); + } +} + +/* ARGSUSED */ +void +ldpe_dispatch_lde(int fd, short event, void *bula) +{ + struct imsgbuf *ibuf = bula; + struct imsg imsg; + struct map map; + int n, shut = 0; + u_int32_t code; + struct nbr *nbr = NULL; + + if (event & EV_READ) { + if ((n = imsg_read(ibuf)) == -1) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + shut = 1; + } + if (event & EV_WRITE) { + if (msgbuf_write(&ibuf->w) == -1) + fatal("msgbuf_write"); + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("ldpe_dispatch_lde: imsg_read error"); + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_MAPPING_ADD: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(map)) + fatalx("invalid size of OE request"); + memcpy(&map, imsg.data, sizeof(map)); + + nbr = nbr_find_peerid(imsg.hdr.peerid); + if (nbr == NULL) { + log_debug("ldpe_dispatch_lde: cannot find " + "neighbor"); + return; + } + + nbr_mapping_add(nbr, &nbr->mapping_list, &map); + break; + case IMSG_MAPPING_ADD_END: + nbr = nbr_find_peerid(imsg.hdr.peerid); + if (nbr == NULL) { + log_debug("ldpe_dispatch_lde: cannot find " + "neighbor"); + return; + } + + send_labelmapping(nbr); + break; + case IMSG_RELEASE_ADD: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(map)) + fatalx("invalid size of OE request"); + memcpy(&map, imsg.data, sizeof(map)); + + nbr = nbr_find_peerid(imsg.hdr.peerid); + if (nbr == NULL) { + log_debug("ldpe_dispatch_lde: cannot find " + "neighbor"); + return; + } + + nbr_mapping_add(nbr, &nbr->release_list, &map); + break; + case IMSG_RELEASE_ADD_END: + nbr = nbr_find_peerid(imsg.hdr.peerid); + if (nbr == NULL) { + log_debug("ldpe_dispatch_lde: cannot find " + "neighbor"); + return; + } + send_labelrelease(nbr); + break; + case IMSG_NOTIFICATION_SEND: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(code)) + fatalx("invalid size of OE request"); + memcpy(&code, imsg.data, sizeof(code)); + + nbr = nbr_find_peerid(imsg.hdr.peerid); + if (nbr == NULL) { + log_debug("ldpe_dispatch_lde: cannot find " + "neighbor"); + return; + } + + send_notification_nbr(nbr, code); + break; + case IMSG_REQUEST_ADD: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(map)) + fatalx("invalid size of OE request"); + memcpy(&map, imsg.data, sizeof(map)); + + nbr = nbr_find_peerid(imsg.hdr.peerid); + if (nbr == NULL) { + log_debug("ldpe_dispatch_lde: cannot find " + "neighbor"); + return; + } + + nbr_mapping_add(nbr, &nbr->request_list, &map); + break; + case IMSG_REQUEST_ADD_END: + nbr = nbr_find_peerid(imsg.hdr.peerid); + if (nbr == NULL) { + log_debug("ldpe_dispatch_lde: cannot find " + "neighbor"); + return; + } + send_labelrequest(nbr); + break; + case IMSG_CTL_END: + case IMSG_CTL_SHOW_LIB: + control_imsg_relay(&imsg); + break; + default: + log_debug("ldpe_dispatch_lde: error handling imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + if (!shut) + imsg_event_add(ibuf); + else { + /* this pipe is dead, so remove the event handler */ + event_del(&ibuf->ev); + event_loopexit(NULL); + } +} + +u_int32_t +ldpe_router_id(void) +{ + return (leconf->rtr_id.s_addr); +} + +void +ldpe_fib_update(int type) +{ + if (type == IMSG_CTL_LFIB_COUPLE) + oe_nofib = 0; + if (type == IMSG_CTL_LFIB_DECOUPLE) + oe_nofib = 1; +} + +void +ldpe_iface_ctl(struct ctl_conn *c, unsigned int idx) +{ + struct iface *iface; + struct ctl_iface *ictl; + + LIST_FOREACH(iface, &leconf->iface_list, entry) { + if (idx == 0 || idx == iface->ifindex) { + ictl = if_to_ctl(iface); + imsg_compose(&c->ibuf, IMSG_CTL_SHOW_INTERFACE, + 0, 0, ictl, sizeof(struct ctl_iface)); + } + } +} + +void +ldpe_nbr_ctl(struct ctl_conn *c) +{ + struct iface *iface; + struct nbr *nbr; + struct ctl_nbr *nctl; + + LIST_FOREACH(iface, &leconf->iface_list, entry) { + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (iface->self != nbr) { + nctl = nbr_to_ctl(nbr); + imsg_compose(&c->ibuf, + IMSG_CTL_SHOW_NBR, 0, 0, nctl, + sizeof(struct ctl_nbr)); + } + } + } + + imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, NULL, 0); +} diff --git a/usr.sbin/ldpd/ldpe.h b/usr.sbin/ldpd/ldpe.h new file mode 100644 index 00000000000..06ba54798d8 --- /dev/null +++ b/usr.sbin/ldpd/ldpe.h @@ -0,0 +1,214 @@ +/* $OpenBSD: ldpe.h,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LDPE_H_ +#define _LDPE_H_ + +#define max(x,y) ((x) > (y) ? (x) : (y)) + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> + +TAILQ_HEAD(ctl_conns, ctl_conn) ctl_conns; + +struct mapping_entry { + TAILQ_ENTRY(mapping_entry) entry; + u_int32_t label; + u_int32_t prefix; + u_int8_t prefixlen; +}; + +struct nbr { + LIST_ENTRY(nbr) entry, hash; + struct bufferevent *bev; + struct event inactivity_timer; + struct event keepalive_timer; + struct event keepalive_timeout; + struct event initdelay_timer; + + struct mapping_head mapping_list; + struct mapping_head withdraw_list; + struct mapping_head request_list; + struct mapping_head release_list; + struct mapping_head abortreq_list; + + int fd; + + struct in_addr addr; + struct in_addr id; + + struct iface *iface; + + time_t uptime; + + u_int32_t peerid; /* unique ID in DB */ + + int state; + + u_int16_t lspace; + u_int16_t holdtime; + u_int16_t keepalive; + + u_int8_t priority; + u_int8_t options; + + u_int8_t flags; + u_int8_t hello_type; + +}; + +/* hello.c */ +int send_hello(struct iface *); +void recv_hello(struct iface *, struct in_addr, char *, u_int16_t); + +/* init.c */ +int send_init(struct nbr *); +int recv_init(struct nbr *, char *, u_int16_t); + +/* keepalive.c */ +int send_keepalive(struct nbr *); +int recv_keepalive(struct nbr *, char *, u_int16_t); + +/* notification.c */ +int send_notification(int, struct iface *, int); +int send_notification_nbr(struct nbr *, u_int32_t); +int recv_notification(struct nbr *, char *, u_int16_t); + +/* address.c */ +int send_address(struct nbr *, struct iface *); +int recv_address(struct nbr *, char *, u_int16_t); +int send_address_withdraw(struct nbr *, struct iface *); +int recv_address_withdraw(struct nbr *, char *, u_int16_t); + +/* labelmapping.c */ +int send_labelmapping(struct nbr *); +int recv_labelmapping(struct nbr *, char *, u_int16_t); +int send_labelrequest(struct nbr *); +int recv_labelrequest(struct nbr *, char *, u_int16_t); +int send_labelwithdraw(struct nbr *); +int recv_labelwithdraw(struct nbr *, char *, u_int16_t); +int send_labelrelease(struct nbr *); +int recv_labelrelease(struct nbr *, char *, u_int16_t); +int send_labelabortreq(struct nbr *); +int recv_labelabortreq(struct nbr *, char *, u_int16_t); + +/* ldpe.c */ +pid_t ldpe(struct ldpd_conf *, int[2], int[2], int[2]); +void ldpe_dispatch_main(int, short, void *); +void ldpe_dispatch_lde(int, short, void *); +int ldpe_imsg_compose_parent(int, pid_t, void *, u_int16_t); +int ldpe_imsg_compose_lde(int, u_int32_t, pid_t, void *, + u_int16_t); +u_int32_t ldpe_router_id(void); +void ldpe_fib_update(int); +void ldpe_iface_ctl(struct ctl_conn *, unsigned int); +void ldpe_nbr_ctl(struct ctl_conn *); + +/* interface.c */ +int if_fsm(struct iface *, enum iface_event); + +struct iface *if_new(struct kif *, struct kif_addr *); +void if_del(struct iface *); +void if_init(struct ldpd_conf *, struct iface *); + +int if_act_start(struct iface *); +int if_act_elect(struct iface *); +int if_act_reset(struct iface *); + +struct ctl_iface *if_to_ctl(struct iface *); + +int if_join_group(struct iface *, struct in_addr *); +int if_leave_group(struct iface *, struct in_addr *); +int if_set_mcast(struct iface *); +int if_set_recvif(int, int); +void if_set_recvbuf(int); +int if_set_mcast_loop(int); +int if_set_mcast_ttl(int, u_int8_t); +int if_set_tos(int, int); +int if_set_reuse(int, int); +int if_set_nonblock(int); + +/* neighbor.c */ +void nbr_init(u_int32_t); +struct nbr *nbr_new(u_int32_t, u_int16_t, struct iface *, int); +void nbr_del(struct nbr *); + +struct nbr *nbr_find_ip(struct iface *, u_int32_t); +struct nbr *nbr_find_ldpid(struct iface *, u_int32_t, u_int16_t); +struct nbr *nbr_find_peerid(u_int32_t); + +int nbr_fsm(struct nbr *, enum nbr_event); + +void nbr_itimer(int, short, void *); +void nbr_start_itimer(struct nbr *); +void nbr_stop_itimer(struct nbr *); +void nbr_reset_itimer(struct nbr *); +void nbr_ktimer(int, short, void *); +void nbr_start_ktimer(struct nbr *); +void nbr_stop_ktimer(struct nbr *); +void nbr_reset_ktimer(struct nbr *); +void nbr_ktimeout(int, short, void *); +void nbr_start_ktimeout(struct nbr *); +void nbr_stop_ktimeout(struct nbr *); +void nbr_reset_ktimeout(struct nbr *); +void nbr_idtimer(int, short, void *); +void nbr_start_idtimer(struct nbr *); +void nbr_stop_idtimer(struct nbr *); +void nbr_reset_idtimer(struct nbr *); +int nbr_pending_idtimer(struct nbr *); + +int nbr_act_reset_itimer(struct nbr *); +int nbr_act_start_itimer(struct nbr *); +int nbr_act_reset_ktimer(struct nbr *); +int nbr_act_start_ktimer(struct nbr *); +int nbr_act_reset_ktimeout(struct nbr *); +int nbr_act_start_ktimeout(struct nbr *); +int nbr_act_reset_idtimer(struct nbr *); +int nbr_act_start_idtimer(struct nbr *); +int nbr_act_session_establish(struct nbr *, int); +int nbr_close_connection(struct nbr *); + +void nbr_mapping_add(struct nbr *, struct mapping_head *, + struct map *); +struct mapping_entry *nbr_mapping_find(struct nbr *, struct mapping_head *, + struct map *); +void nbr_mapping_del(struct nbr *, struct mapping_head *, + struct map *); +void nbr_mapping_list_clr(struct nbr *, + struct mapping_head *); + +struct ctl_nbr *nbr_to_ctl(struct nbr *); + +/* packet.c */ +int gen_ldp_hdr(struct buf *, struct iface *, u_int16_t); +int gen_msg_tlv(struct buf *, u_int32_t, u_int16_t); +int send_packet(struct iface *, void *, size_t, struct sockaddr_in *); +void disc_recv_packet(int, short, void *); +void session_recv_packet(int, short, void *); + +void session_read(struct bufferevent *, void *); +void session_write(struct bufferevent *, void *); +void session_error(struct bufferevent *, short, void *); +void session_close(struct nbr *); + +char *pkt_ptr; /* packet buffer */ + +#endif /* _LDPE_H_ */ diff --git a/usr.sbin/ldpd/log.c b/usr.sbin/ldpd/log.c new file mode 100644 index 00000000000..1e97d6f1a23 --- /dev/null +++ b/usr.sbin/ldpd/log.c @@ -0,0 +1,225 @@ +/* $OpenBSD: log.c,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include "ldpd.h" +#include "log.h" + +static const char * const procnames[] = { + "parent", + "ldpe", + "lde" +}; + +int debug; + +void logit(int, const char *, ...); + +void +log_init(int n_debug) +{ + extern char *__progname; + + debug = n_debug; + + if (!debug) + openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); + + tzset(); +} + +void +logit(int pri, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vlog(pri, fmt, ap); + va_end(ap); +} + +void +vlog(int pri, const char *fmt, va_list ap) +{ + char *nfmt; + + if (debug) { + /* best effort in out of mem situations */ + if (asprintf(&nfmt, "%s\n", fmt) == -1) { + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + } else { + vfprintf(stderr, nfmt, ap); + free(nfmt); + } + fflush(stderr); + } else + vsyslog(pri, fmt, ap); +} + +void +log_warn(const char *emsg, ...) +{ + char *nfmt; + va_list ap; + + /* best effort to even work in out of memory situations */ + if (emsg == NULL) + logit(LOG_CRIT, "%s", strerror(errno)); + else { + va_start(ap, emsg); + + if (asprintf(&nfmt, "%s: %s", emsg, strerror(errno)) == -1) { + /* we tried it... */ + vlog(LOG_CRIT, emsg, ap); + logit(LOG_CRIT, "%s", strerror(errno)); + } else { + vlog(LOG_CRIT, nfmt, ap); + free(nfmt); + } + va_end(ap); + } +} + +void +log_warnx(const char *emsg, ...) +{ + va_list ap; + + va_start(ap, emsg); + vlog(LOG_CRIT, emsg, ap); + va_end(ap); +} + +void +log_info(const char *emsg, ...) +{ + va_list ap; + + va_start(ap, emsg); + vlog(LOG_INFO, emsg, ap); + va_end(ap); +} + +void +log_debug(const char *emsg, ...) +{ + va_list ap; + + if (debug) { + va_start(ap, emsg); + vlog(LOG_DEBUG, emsg, ap); + va_end(ap); + } +} + +void +fatal(const char *emsg) +{ + if (emsg == NULL) + logit(LOG_CRIT, "fatal in %s: %s", procnames[ldpd_process], + strerror(errno)); + else + if (errno) + logit(LOG_CRIT, "fatal in %s: %s: %s", + procnames[ldpd_process], emsg, strerror(errno)); + else + logit(LOG_CRIT, "fatal in %s: %s", + procnames[ldpd_process], emsg); + + if (ldpd_process == PROC_MAIN) + exit(1); + else /* parent copes via SIGCHLD */ + _exit(1); +} + +void +fatalx(const char *emsg) +{ + errno = 0; + fatal(emsg); +} + +/* names */ +const char * +nbr_state_name(int state) +{ + switch (state) { + case NBR_STA_DOWN: + return ("DOWN"); + case NBR_STA_PRESENT: + return ("PRESENT"); + case NBR_STA_INITIAL: + return ("INITIALIZED"); + case NBR_STA_OPENREC: + return ("OPENREC"); + case NBR_STA_OPENSENT: + return ("OPENSENT"); + case NBR_STA_OPER: + return ("OPERATIONAL"); + case NBR_STA_ACTIVE: + return ("ACTIVE"); + default: + return ("UNKNW"); + } +} + +const char * +if_state_name(int state) +{ + switch (state) { + case IF_STA_DOWN: + return ("DOWN"); + case IF_STA_LOOPBACK: + return ("LOOP"); + case IF_STA_POINTTOPOINT: + return ("P2P"); + case IF_STA_DROTHER: + return ("OTHER"); + case IF_STA_ACTIVE: + return ("ACTIVE"); + default: + return ("UNKNW"); + } +} + +const char * +if_type_name(enum iface_type type) +{ + switch (type) { + case IF_TYPE_POINTOPOINT: + return ("POINTOPOINT"); + case IF_TYPE_BROADCAST: + return ("BROADCAST"); + case IF_TYPE_NBMA: + return ("NBMA"); + case IF_TYPE_POINTOMULTIPOINT: + return ("POINTOMULTIPOINT"); + case IF_TYPE_VIRTUALLINK: + return ("VIRTUALLINK"); + } + /* NOTREACHED */ + return ("UNKNOWN"); +} diff --git a/usr.sbin/ldpd/log.h b/usr.sbin/ldpd/log.h new file mode 100644 index 00000000000..7fea7d99b30 --- /dev/null +++ b/usr.sbin/ldpd/log.h @@ -0,0 +1,33 @@ +/* $OpenBSD: log.h,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LOG_H_ +#define _LOG_H_ + +#include <stdarg.h> + +void log_init(int); +void vlog(int, const char *, va_list); +void log_warn(const char *, ...); +void log_warnx(const char *, ...); +void log_info(const char *, ...); +void log_debug(const char *, ...); +void fatal(const char *) __dead; +void fatalx(const char *) __dead; + +#endif /* _LOG_H_ */ diff --git a/usr.sbin/ldpd/name2id.c b/usr.sbin/ldpd/name2id.c new file mode 100644 index 00000000000..23f8ea773a7 --- /dev/null +++ b/usr.sbin/ldpd/name2id.c @@ -0,0 +1,228 @@ +/* $OpenBSD: name2id.c,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2004, 2005 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <net/route.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "ldpd.h" + +#define IDVAL_MAX 50000 + +u_int16_t _name2id(struct n2id_labels *, const char *); +const char *_id2name(struct n2id_labels *, u_int16_t); +u_int32_t _id2tag(struct n2id_labels *, u_int16_t); +u_int16_t _tag2id(struct n2id_labels *, u_int32_t); +void _tag(struct n2id_labels *, u_int16_t, u_int32_t); +void _unref(struct n2id_labels *, u_int16_t); +void _ref(struct n2id_labels *, u_int16_t); + +struct n2id_labels rt_labels = TAILQ_HEAD_INITIALIZER(rt_labels); + +u_int16_t +rtlabel_name2id(const char *name) +{ + return (_name2id(&rt_labels, name)); +} + +const char * +rtlabel_id2name(u_int16_t id) +{ + return (_id2name(&rt_labels, id)); +} + +u_int32_t +rtlabel_id2tag(u_int16_t id) +{ + return (_id2tag(&rt_labels, id)); +} + +u_int16_t +rtlabel_tag2id(u_int32_t tag) +{ + return (_tag2id(&rt_labels, tag)); +} + +void +rtlabel_tag(u_int16_t id, u_int32_t tag) +{ + _tag(&rt_labels, id, tag); +} + +void +rtlabel_unref(u_int16_t id) +{ + _unref(&rt_labels, id); +} + +/* +void +rtlabel_ref(u_int16_t id) +{ + _ref(&rt_labels, id); +} +*/ + +u_int16_t +_name2id(struct n2id_labels *head, const char *name) +{ + struct n2id_label *label, *p = NULL; + u_int16_t new_id = 1; + + if (!name[0]) { + errno = EINVAL; + return (0); + } + + TAILQ_FOREACH(label, head, entry) + if (strcmp(name, label->name) == 0) { + label->ref++; + return (label->id); + } + + /* + * to avoid fragmentation, we do a linear search from the beginning + * and take the first free slot we find. if there is none or the list + * is empty, append a new entry at the end. + */ + + if (!TAILQ_EMPTY(head)) + for (p = TAILQ_FIRST(head); p != NULL && + p->id == new_id; p = TAILQ_NEXT(p, entry)) + new_id = p->id + 1; + + if (new_id > IDVAL_MAX) { + errno = ERANGE; + return (0); + } + + if ((label = calloc(1, sizeof(struct n2id_label))) == NULL) + return (0); + if ((label->name = strdup(name)) == NULL) { + free(label); + return (0); + } + label->id = new_id; + label->ref++; + + if (p != NULL) /* insert new entry before p */ + TAILQ_INSERT_BEFORE(p, label, entry); + else /* either list empty or no free slot in between */ + TAILQ_INSERT_TAIL(head, label, entry); + + return (label->id); +} + +const char * +_id2name(struct n2id_labels *head, u_int16_t id) +{ + struct n2id_label *label; + + if (id == 0) + return (""); + + TAILQ_FOREACH(label, head, entry) + if (label->id == id) + return (label->name); + + return (""); +} + +u_int32_t +_id2tag(struct n2id_labels *head, u_int16_t id) +{ + struct n2id_label *label; + + if (id == 0) + return (0); + + TAILQ_FOREACH(label, head, entry) + if (label->id == id) + return (label->ext_tag); + + return (0); +} + +u_int16_t +_tag2id(struct n2id_labels *head, u_int32_t tag) +{ + struct n2id_label *label; + + if (tag == 0) + return (0); + + TAILQ_FOREACH(label, head, entry) + if (label->ext_tag == tag) + return (label->id); + + return (0); +} + +void +_tag(struct n2id_labels *head, u_int16_t id, u_int32_t tag) +{ + struct n2id_label *label; + + if (id == 0) + return; + + TAILQ_FOREACH(label, head, entry) + if (label->id == id) + label->ext_tag = tag; +} + +void +_unref(struct n2id_labels *head, u_int16_t id) +{ + struct n2id_label *p, *next; + + if (id == 0) + return; + + for (p = TAILQ_FIRST(head); p != NULL; p = next) { + next = TAILQ_NEXT(p, entry); + if (id == p->id) { + if (--p->ref == 0) { + TAILQ_REMOVE(head, p, entry); + free(p->name); + free(p); + } + break; + } + } +} + +void +_ref(struct n2id_labels *head, u_int16_t id) +{ + struct n2id_label *label; + + if (id == 0) + return; + + TAILQ_FOREACH(label, head, entry) + if (label->id == id) { + ++label->ref; + break; + } +} diff --git a/usr.sbin/ldpd/neighbor.c b/usr.sbin/ldpd/neighbor.c new file mode 100644 index 00000000000..123f460bb21 --- /dev/null +++ b/usr.sbin/ldpd/neighbor.c @@ -0,0 +1,767 @@ +/* $OpenBSD: neighbor.c,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <net/if.h> + +#include <ctype.h> +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <event.h> +#include <unistd.h> + +#include "ldpd.h" +#include "ldp.h" +#include "ldpe.h" +#include "log.h" +#include "lde.h" + +int nbr_establish_connection(struct nbr *); +void nbr_send_labelmappings(struct nbr *); + +LIST_HEAD(nbr_head, nbr); + +struct nbr_table { + struct nbr_head *hashtbl; + u_int32_t hashmask; +} nbrtable; + +#define NBR_HASH(x) \ + &nbrtable.hashtbl[(x) & nbrtable.hashmask] + +u_int32_t peercnt = NBR_CNTSTART; + +extern struct ldpd_conf *leconf; + +struct { + int state; + enum nbr_event event; + enum nbr_action action; + int new_state; +} nbr_fsm_tbl[] = { + /* current state event that happened action to take resulting state */ +/* Discovery States */ + {NBR_STA_DOWN, NBR_EVT_HELLO_RCVD, NBR_ACT_STRT_ITIMER, NBR_STA_PRESENT}, + {NBR_STA_UP, NBR_EVT_HELLO_RCVD, NBR_ACT_RST_ITIMER, 0}, +/* Passive Role */ + {NBR_STA_PRESENT, NBR_EVT_SESSION_UP, NBR_ACT_SESSION_EST, NBR_STA_INITIAL}, + {NBR_STA_INITIAL, NBR_EVT_INIT_RCVD, NBR_ACT_INIT_SEND, NBR_STA_OPENREC}, + {NBR_STA_OPENREC, NBR_EVT_KEEPALIVE_RCVD, NBR_ACT_STRT_KTIMER, NBR_STA_OPER}, +/* Active Role */ + {NBR_STA_PRESENT, NBR_EVT_INIT_SENT, NBR_ACT_NOTHING, NBR_STA_OPENSENT}, + {NBR_STA_OPENSENT, NBR_EVT_INIT_RCVD, NBR_ACT_KEEPALIVE_SEND, NBR_STA_OPER}, +/* Session Maintenance */ + {NBR_STA_OPER, NBR_EVT_PDU_RCVD, NBR_ACT_RST_KTIMEOUT, 0}, +/* Session Close */ + {NBR_STA_SESSION, NBR_EVT_CLOSE_SESSION, NBR_ACT_CLOSE_SESSION, NBR_STA_PRESENT}, + {NBR_STA_UP, NBR_EVT_DOWN, NBR_ACT_CLOSE_SESSION, }, + {-1, NBR_EVT_NOTHING, NBR_ACT_NOTHING, 0}, +}; + +const char * const nbr_event_names[] = { + "NOTHING", + "HELLO RECEIVED", + "SESSION UP", + "SESSION CLOSE", + "INIT RECEIVED", + "KEEPALIVE RECEIVED", + "PDU RECEIVED", + "INIT SENT", + "DOWN" +}; + +const char * const nbr_action_names[] = { + "NOTHING", + "START INACTIVITY TIMER", + "RESET INACTIVITY TIMER", + "RESET KEEPALIVE TIMEOUT", + "START KEEPALIVE TIMER", + "RESET KEEPALIVE TIMER", + "START NEIGHBOR SESSION", + "SEND INIT", + "SEND KEEPALIVE", + "CLOSE SESSION" +}; + +int +nbr_fsm(struct nbr *nbr, enum nbr_event event) +{ + int old_state; + int new_state = 0; + int i, ret = 0; + + if (nbr == nbr->iface->self) + return (0); + + old_state = nbr->state; + for (i = 0; nbr_fsm_tbl[i].state != -1; i++) + if ((nbr_fsm_tbl[i].state & old_state) && + (nbr_fsm_tbl[i].event == event)) { + new_state = nbr_fsm_tbl[i].new_state; + break; + } + + if (nbr_fsm_tbl[i].state == -1) { + /* event outside of the defined fsm, ignore it. */ + log_warnx("nbr_fsm: neighbor ID %s, " + "event %s not expected in state %s", + inet_ntoa(nbr->id), nbr_event_names[event], + nbr_state_name(old_state)); + return (0); + } + + switch (nbr_fsm_tbl[i].action) { + case NBR_ACT_RST_ITIMER: + ret = nbr_act_reset_itimer(nbr); + break; + case NBR_ACT_STRT_ITIMER: + ret = nbr_act_start_itimer(nbr); + break; + case NBR_ACT_RST_KTIMEOUT: + ret = nbr_act_reset_ktimeout(nbr); + break; + case NBR_ACT_RST_KTIMER: + ret = nbr_act_reset_ktimer(nbr); + break; + case NBR_ACT_STRT_KTIMER: + /* XXX */ + ret = nbr_act_start_ktimer(nbr); + nbr_act_start_ktimeout(nbr); + send_address(nbr, NULL); + nbr_send_labelmappings(nbr); + break; + case NBR_ACT_SESSION_EST: + ret = nbr_act_session_establish(nbr, 0); + break; + case NBR_ACT_INIT_SEND: + /* XXX */ + send_keepalive(nbr); + ret = send_init(nbr); + break; + case NBR_ACT_KEEPALIVE_SEND: + /* XXX */ + ret = nbr_act_start_ktimer(nbr); + nbr_act_start_ktimeout(nbr); + send_keepalive(nbr); + send_address(nbr, NULL); + nbr_send_labelmappings(nbr); + break; + case NBR_ACT_CLOSE_SESSION: + session_close(nbr); + ret = nbr_act_start_idtimer(nbr); + break; + case NBR_ACT_NOTHING: + /* do nothing */ + break; + } + + if (ret) { + log_warnx("nbr_fsm: error changing state for neighbor ID %s, " + "event %s, state %s", inet_ntoa(nbr->id), + nbr_event_names[event], nbr_state_name(old_state)); + return (-1); + } + + if (new_state != 0) + nbr->state = new_state; + + if (old_state != nbr->state) { + log_debug("nbr_fsm: event %s resulted in action %s and " + "changing state for neighbor ID %s from %s to %s", + nbr_event_names[event], + nbr_action_names[nbr_fsm_tbl[i].action], + inet_ntoa(nbr->id), nbr_state_name(old_state), + nbr_state_name(nbr->state)); + } + + return (ret); +} + +void +nbr_init(u_int32_t hashsize) +{ + struct nbr_head *head; + struct nbr *nbr; + u_int32_t hs, i; + + for (hs = 1; hs < hashsize; hs <<= 1) + ; + nbrtable.hashtbl = calloc(hs, sizeof(struct nbr_head)); + if (nbrtable.hashtbl == NULL) + fatal("nbr_init"); + + for (i = 0; i < hs; i++) + LIST_INIT(&nbrtable.hashtbl[i]); + + nbrtable.hashmask = hs - 1; + + /* allocate a dummy neighbor used for self originated AS ext routes */ + if ((nbr = calloc(1, sizeof(*nbr))) == NULL) + fatal("nbr_init"); + + nbr->id.s_addr = ldpe_router_id(); + nbr->state = NBR_STA_DOWN; + nbr->peerid = NBR_IDSELF; + head = NBR_HASH(nbr->peerid); + LIST_INSERT_HEAD(head, nbr, hash); + + TAILQ_INIT(&nbr->mapping_list); + TAILQ_INIT(&nbr->withdraw_list); + TAILQ_INIT(&nbr->request_list); + TAILQ_INIT(&nbr->release_list); + TAILQ_INIT(&nbr->abortreq_list); +} + +struct nbr * +nbr_new(u_int32_t nbr_id, u_int16_t lspace, struct iface *iface, int self) +{ + struct nbr_head *head; + struct nbr *nbr; + struct lde_nbr rn; + + if ((nbr = calloc(1, sizeof(*nbr))) == NULL) + fatal("nbr_new"); + + nbr->state = NBR_STA_DOWN; + nbr->id.s_addr = nbr_id; + nbr->lspace = lspace; + + /* get next unused peerid */ + while (nbr_find_peerid(++peercnt)) + ; + nbr->peerid = peercnt; + head = NBR_HASH(nbr->peerid); + LIST_INSERT_HEAD(head, nbr, hash); + + /* add to peer list */ + nbr->iface = iface; + LIST_INSERT_HEAD(&iface->nbr_list, nbr, entry); + + if (self) { + nbr->addr.s_addr = iface->addr.s_addr; + nbr->priority = iface->priority; + } + + /* set event structures */ + evtimer_set(&nbr->inactivity_timer, nbr_itimer, nbr); + evtimer_set(&nbr->keepalive_timeout, nbr_ktimeout, nbr); + evtimer_set(&nbr->keepalive_timer, nbr_ktimer, nbr); + evtimer_set(&nbr->initdelay_timer, nbr_idtimer, nbr); + + bzero(&rn, sizeof(rn)); + rn.id.s_addr = nbr->id.s_addr; + rn.lspace = nbr->lspace; + rn.ifindex = nbr->iface->ifindex; + rn.self = self; + ldpe_imsg_compose_lde(IMSG_NEIGHBOR_UP, nbr->peerid, 0, &rn, + sizeof(rn)); + + return (nbr); +} + +void +nbr_del(struct nbr *nbr) +{ + ldpe_imsg_compose_lde(IMSG_NEIGHBOR_DOWN, nbr->peerid, 0, NULL, 0); + + if (evtimer_pending(&nbr->inactivity_timer, NULL)) + evtimer_del(&nbr->inactivity_timer); + if (evtimer_pending(&nbr->keepalive_timer, NULL)) + evtimer_del(&nbr->keepalive_timer); + if (evtimer_pending(&nbr->keepalive_timeout, NULL)) + evtimer_del(&nbr->keepalive_timeout); + if (evtimer_pending(&nbr->initdelay_timer, NULL)) + evtimer_del(&nbr->initdelay_timer); + + nbr_mapping_list_clr(nbr, &nbr->mapping_list); + + LIST_REMOVE(nbr, entry); + LIST_REMOVE(nbr, hash); + + free(nbr); +} + +struct nbr * +nbr_find_peerid(u_int32_t peerid) +{ + struct nbr_head *head; + struct nbr *nbr; + + head = NBR_HASH(peerid); + + LIST_FOREACH(nbr, head, hash) { + if (nbr->peerid == peerid) + return (nbr); + } + + return (NULL); +} + +struct nbr * +nbr_find_ip(struct iface *iface, u_int32_t rtr_id) +{ + struct nbr *nbr = NULL; + + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (nbr->addr.s_addr == rtr_id) + return (nbr); + } + + return (NULL); +} + +struct nbr * +nbr_find_ldpid(struct iface *iface, u_int32_t rtr_id, u_int16_t lspace) +{ + struct nbr *nbr = NULL; + + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (nbr->id.s_addr == rtr_id && nbr->lspace == lspace) + return (nbr); + } + + return (NULL); +} + +/* timers */ + +/* Inactivity timer: timeout based on hellos */ +/* ARGSUSED */ +void +nbr_itimer(int fd, short event, void *arg) +{ + struct nbr *nbr = arg; + + log_debug("nbr_itimer: neighbor ID %s peerid %lu", inet_ntoa(nbr->id), + nbr->peerid); + + nbr_del(nbr); +} + +void +nbr_start_itimer(struct nbr *nbr) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = nbr->holdtime; + + if (evtimer_add(&nbr->inactivity_timer, &tv) == -1) + fatal("nbr_start_itimer"); +} + +void +nbr_stop_itimer(struct nbr *nbr) +{ + if (evtimer_del(&nbr->inactivity_timer) == -1) + fatal("nbr_stop_itimer"); +} + +void +nbr_reset_itimer(struct nbr *nbr) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = nbr->holdtime; + + if (evtimer_add(&nbr->inactivity_timer, &tv) == -1) + fatal("nbr_reset_itimer"); +} + +/* Keepalive timer: timer to send keepalive message to neighbors */ + +void +nbr_ktimer(int fd, short event, void *arg) +{ + struct nbr *nbr = arg; + struct timeval tv; + + log_debug("nbr_ktimer: neighbor ID %s peerid %lu", inet_ntoa(nbr->id), + nbr->peerid); + + send_keepalive(nbr); + + timerclear(&tv); + tv.tv_sec = (time_t)(nbr->keepalive / KEEPALIVE_PER_PERIOD); + if (evtimer_add(&nbr->keepalive_timer, &tv) == -1) + fatal("nbr_ktimer"); +} + +void +nbr_start_ktimer(struct nbr *nbr) +{ + struct timeval tv; + + timerclear(&tv); + + /* XXX: just to be sure it will send three keepalives per period */ + tv.tv_sec = (time_t)(nbr->keepalive / KEEPALIVE_PER_PERIOD); + + if (evtimer_add(&nbr->keepalive_timer, &tv) == -1) + fatal("nbr_start_ktimer"); +} + +void +nbr_stop_ktimer(struct nbr *nbr) +{ + if (evtimer_del(&nbr->keepalive_timer) == -1) + fatal("nbr_stop_ktimer"); +} + +void +nbr_reset_ktimer(struct nbr *nbr) +{ + struct timeval tv; + + timerclear(&tv); + + /* XXX: just to be sure it will send three keepalives per period */ + tv.tv_sec = (time_t)(nbr->keepalive / KEEPALIVE_PER_PERIOD); + + if (evtimer_add(&nbr->keepalive_timer, &tv) == -1) + fatal("nbr_reset_ktimer"); +} + +/* Keepalive timeout: if the nbr hasn't sent keepalive */ + +void +nbr_ktimeout(int fd, short event, void *arg) +{ + struct nbr *nbr = arg; + + log_debug("nbr_ktimeout: neighbor ID %s peerid %lu", inet_ntoa(nbr->id), + nbr->peerid); + + send_notification_nbr(nbr, S_KEEPALIVE_TMR); + close(nbr->fd); +} + +void +nbr_start_ktimeout(struct nbr *nbr) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = nbr->keepalive; + + if (evtimer_add(&nbr->keepalive_timeout, &tv) == -1) + fatal("nbr_start_ktimeout"); +} + +void +nbr_stop_ktimeout(struct nbr *nbr) +{ + if (evtimer_del(&nbr->keepalive_timeout) == -1) + fatal("nbr_stop_ktimeout"); +} + +void +nbr_reset_ktimeout(struct nbr *nbr) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = nbr->keepalive; + + if (evtimer_add(&nbr->keepalive_timeout, &tv) == -1) + fatal("nbr_reset_ktimeout"); +} + +/* Init delay timer: timer to retry to iniziatize session */ + +void +nbr_idtimer(int fd, short event, void *arg) +{ + struct nbr *nbr = arg; + + log_debug("nbr_idtimer: neighbor ID %s peerid %lu", inet_ntoa(nbr->id), + nbr->peerid); + + nbr_act_session_establish(nbr, 1); +} + +void +nbr_start_idtimer(struct nbr *nbr) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = INIT_DELAY_TMR; + + if (evtimer_add(&nbr->initdelay_timer, &tv) == -1) + fatal("nbr_start_idtimer"); +} + +void +nbr_stop_idtimer(struct nbr *nbr) +{ + if (evtimer_del(&nbr->initdelay_timer) == -1) + fatal("nbr_stop_idtimer"); +} + +int +nbr_pending_idtimer(struct nbr *nbr) +{ + if (evtimer_pending(&nbr->initdelay_timer, NULL)) + return (1); + + return (0); +} + + +void +nbr_reset_idtimer(struct nbr *nbr) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = INIT_DELAY_TMR; + + if (evtimer_add(&nbr->initdelay_timer, &tv) == -1) + fatal("nbr_reset_idtimer"); +} +/* actions */ +int +nbr_act_reset_itimer(struct nbr *nbr) +{ + nbr_reset_itimer(nbr); + + return (0); +} + +int +nbr_act_start_itimer(struct nbr *nbr) +{ + nbr_start_itimer(nbr); + + return (0); +} + +int +nbr_act_reset_ktimer(struct nbr *nbr) +{ + nbr_reset_ktimer(nbr); + + return (0); +} + +int +nbr_act_start_ktimer(struct nbr *nbr) +{ + nbr_start_ktimer(nbr); + + return (0); +} + +int +nbr_act_reset_ktimeout(struct nbr *nbr) +{ + nbr_reset_ktimeout(nbr); + + return (0); +} + +int +nbr_act_start_ktimeout(struct nbr *nbr) +{ + nbr_start_ktimeout(nbr); + + return (0); +} + +int +nbr_act_reset_idtimer(struct nbr *nbr) +{ + nbr_reset_idtimer(nbr); + + return (0); +} + +int +nbr_act_start_idtimer(struct nbr *nbr) +{ + if (nbr->addr.s_addr < nbr->iface->addr.s_addr) + nbr_start_idtimer(nbr); + + return (0); +} + +int +nbr_establish_connection(struct nbr *nbr) +{ + struct sockaddr_in in; + int st; + + in.sin_family = AF_INET; + in.sin_port = htons(LDP_PORT); + in.sin_addr.s_addr = nbr->addr.s_addr; + + nbr->fd = socket(AF_INET, SOCK_STREAM, 0); + if (nbr->fd < 0) { + log_debug("nbr_establish_connection: error while " + "creating socket"); + return (-1); + } + + st = connect(nbr->fd, (struct sockaddr *)&in, sizeof(in)); + if (st < 0 ) { + log_debug("nbr_establish_connection: error while " + "connecting to %s", inet_ntoa(nbr->addr)); + nbr_act_start_idtimer(nbr); + return (-1); + } + + return (0); +} + +int +nbr_close_connection(struct nbr *nbr) +{ + bufferevent_disable(nbr->bev, EV_READ|EV_WRITE); + bufferevent_free(nbr->bev); + close(nbr->fd); + + return (0); +} + +int +nbr_act_session_establish(struct nbr *nbr, int active) +{ + evbuffercb readfn = session_read; + everrorcb errorfn = session_error; + + if (active) { + if (nbr_establish_connection(nbr) < 0) + return (-1); + } + + nbr->bev = bufferevent_new(nbr->fd, readfn, NULL, errorfn, nbr); + bufferevent_settimeout(nbr->bev, 0, 0); + bufferevent_enable(nbr->bev, EV_READ|EV_WRITE); + + if (active) { + send_init(nbr); + nbr_fsm(nbr, NBR_EVT_INIT_SENT); + } + + + return (0); +} + +void +nbr_send_labelmappings(struct nbr *nbr) +{ + if (leconf->mode & MODE_ADV_UNSOLICITED) { + ldpe_imsg_compose_lde(IMSG_LABEL_MAPPING_FULL, nbr->peerid, 0, + 0, 0); + } +} + +void +nbr_mapping_add(struct nbr *nbr, struct mapping_head *mh, struct map *map) +{ + struct mapping_entry *me; + + me = calloc(1, sizeof(*me)); + if (me == NULL) + fatal("nbr_mapping_add"); + + me->prefix = map->prefix; + me->prefixlen = map->prefixlen; + me->label = map->label; + + TAILQ_INSERT_HEAD(mh, me, entry); +} + +struct mapping_entry * +nbr_mapping_find(struct nbr *nbr, struct mapping_head *mh, struct map *map) +{ + struct mapping_entry *me = NULL; + + TAILQ_FOREACH(me, mh, entry) { + if (me->prefix == map->prefix && + me->prefixlen == map->prefixlen) + return (me); + } + + return (NULL); +} + +void +nbr_mapping_del(struct nbr *nbr, struct mapping_head *mh, struct map *map) +{ + struct mapping_entry *me; + + me = nbr_mapping_find(nbr, mh, map); + if (me == NULL) + return; + + TAILQ_REMOVE(mh, me, entry); + free(me); +} + +void +nbr_mapping_list_clr(struct nbr *nbr, struct mapping_head *mh) +{ + struct mapping_entry *me; + + while ((me = TAILQ_FIRST(mh)) != NULL) { + TAILQ_REMOVE(mh, me, entry); + free(me); + } +} + +struct ctl_nbr * +nbr_to_ctl(struct nbr *nbr) +{ + static struct ctl_nbr nctl; + struct timeval tv, now, res; + + memcpy(nctl.name, nbr->iface->name, sizeof(nctl.name)); + memcpy(&nctl.id, &nbr->id, sizeof(nctl.id)); + memcpy(&nctl.addr, &nbr->addr, sizeof(nctl.addr)); + + nctl.nbr_state = nbr->state; + + gettimeofday(&now, NULL); + if (evtimer_pending(&nbr->inactivity_timer, &tv)) { + timersub(&tv, &now, &res); + if (nbr->state & NBR_STA_DOWN) + nctl.dead_timer = DEFAULT_NBR_TMOUT - res.tv_sec; + else + nctl.dead_timer = res.tv_sec; + } else + nctl.dead_timer = 0; +/* + if (nbr->state == NBR_STA_FULL) { + nctl.uptime = now.tv_sec - nbr->uptime; + } else + nctl.uptime = 0; +*/ + return (&nctl); +} diff --git a/usr.sbin/ldpd/notification.c b/usr.sbin/ldpd/notification.c new file mode 100644 index 00000000000..01613fbf570 --- /dev/null +++ b/usr.sbin/ldpd/notification.c @@ -0,0 +1,133 @@ +/* $OpenBSD: notification.c,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <net/if_dl.h> +#include <unistd.h> + +#include <errno.h> +#include <event.h> +#include <stdlib.h> +#include <string.h> + +#include "ldpd.h" +#include "ldp.h" +#include "log.h" +#include "ldpe.h" + +int gen_status_tlv(struct buf *, int); + +int +send_notification_nbr(struct nbr *nbr, u_int32_t status) +{ + if (nbr->iface->passive) + return (0); + + log_debug("send_notification: neighbor ID %s", inet_ntoa(nbr->id)); + + return (send_notification(status, nbr->iface, nbr->fd)); +} + +int +send_notification(int status, struct iface *iface, int fd) +{ + struct buf *buf; + u_int16_t size; + + if ((buf = buf_open(LDP_MAX_LEN)) == NULL) + fatal("send_notification"); + + size = LDP_HDR_SIZE + sizeof(struct ldp_msg) + STATUS_SIZE; + + gen_ldp_hdr(buf, iface, size); + + size -= LDP_HDR_SIZE; + + gen_msg_tlv(buf, MSG_TYPE_NOTIFICATION, size); + + size -= sizeof(struct ldp_msg); + + gen_status_tlv(buf, status); + + write(fd, buf->buf, buf->wpos); + buf_free(buf); + + return (0); +} + +int +recv_notification(struct nbr *nbr, char *buf, u_int16_t len) +{ + struct ldp_msg *not; + struct status_tlv *st; + u_int32_t messageid; + + log_debug("recv_notification: neighbor ID %s", inet_ntoa(nbr->id)); + + not = (struct ldp_msg *)buf; + + if ((len - TLV_HDR_LEN) < ntohs(not->length)) { + /* XXX: send notification */ + return (-1); + } + + messageid = not->msgid; + + buf += sizeof(struct ldp_msg); + len -= sizeof(struct ldp_msg); + + st = (struct status_tlv *)buf; + + if (len < STATUS_SIZE || + (STATUS_SIZE - TLV_HDR_LEN) != ntohs(st->length)) { + /* XXX: send notification */ + return (-1); + } + + if (st->status_code & htonl(STATUS_FATAL)) { + nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION); + return (-1); + } + + return (ntohs(not->length)); +} + +int +gen_status_tlv(struct buf *buf, int status) +{ + struct status_tlv st; + + bzero(&st, sizeof(st)); + + st.type = htons(TLV_TYPE_STATUS); + st.length = htons(STATUS_TLV_LEN); + st.status_code = htonl(status); + + /* XXX */ + st.msg_id = 0; + st.msg_type = 0; + + return (buf_add(buf, &st, STATUS_SIZE)); +} diff --git a/usr.sbin/ldpd/packet.c b/usr.sbin/ldpd/packet.c new file mode 100644 index 00000000000..41e18fdc215 --- /dev/null +++ b/usr.sbin/ldpd/packet.c @@ -0,0 +1,463 @@ +/* $OpenBSD: packet.c,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <net/if_dl.h> +#include <fcntl.h> +#include <unistd.h> + +#include <errno.h> +#include <event.h> +#include <stdlib.h> +#include <string.h> + +#include "ldpd.h" +#include "ldp.h" +#include "log.h" +#include "ldpe.h" + +int ldp_hdr_sanity_check(struct ldp_hdr *, u_int16_t, + const struct iface *); +struct iface *find_iface(struct ldpd_conf *, unsigned int, struct in_addr); +struct iface *session_find_iface(struct ldpd_conf *, struct in_addr); + +static int msgcnt = 0; + +int +gen_ldp_hdr(struct buf *buf, struct iface *iface, u_int16_t size) +{ + struct ldp_hdr ldp_hdr; + + bzero(&ldp_hdr, sizeof(ldp_hdr)); + ldp_hdr.version = htons(LDP_VERSION); + + /* We want just the size of the value */ + size -= TLV_HDR_LEN; + + ldp_hdr.length = htons(size); + ldp_hdr.lsr_id = ldpe_router_id(); + ldp_hdr.lspace_id = iface->lspace_id; + + return (buf_add(buf, &ldp_hdr, LDP_HDR_SIZE)); +} + +int +gen_msg_tlv(struct buf *buf, u_int32_t type, u_int16_t size) +{ + struct ldp_msg msg; + + /* We want just the size of the value */ + size -= TLV_HDR_LEN; + + bzero(&msg, sizeof(msg)); + msg.type = htons(type); + msg.length = htons(size); + msg.msgid = htonl(++msgcnt); + + return (buf_add(buf, &msg, sizeof(msg))); +} + +/* send and receive packets */ +int +send_packet(struct iface *iface, void *pkt, size_t len, struct sockaddr_in *dst) +{ + /* set outgoing interface for multicast traffic */ + if (IN_MULTICAST(ntohl(dst->sin_addr.s_addr))) + if (if_set_mcast(iface) == -1) { + log_warn("send_packet: error setting multicast " + "interface, %s", iface->name); + return (-1); + } + + if (sendto(iface->discovery_fd, pkt, len, 0, + (struct sockaddr *)dst, sizeof(*dst)) == -1) { + log_warn("send_packet: error sending packet on interface %s", + iface->name); + return (-1); + } + + return (0); +} + +/* Discovery functions */ +void +disc_recv_packet(int fd, short event, void *bula) +{ + union { + struct cmsghdr hdr; + char buf[CMSG_SPACE(sizeof(struct sockaddr_dl))]; + } cmsgbuf; + struct sockaddr_in src; + struct msghdr msg; + struct iovec iov; + struct ldpd_conf *xconf = bula; + struct ldp_hdr *ldp_hdr; + struct ldp_msg *ldp_msg; + struct iface *iface; + char *buf; + struct cmsghdr *cmsg; + ssize_t r; + u_int16_t len; + int l; + unsigned int ifindex = 0; + + if (event != EV_READ) + return; + + /* setup buffer */ + bzero(&msg, sizeof(msg)); + iov.iov_base = buf = pkt_ptr; + iov.iov_len = READ_BUF_SIZE; + msg.msg_name = &src; + msg.msg_namelen = sizeof(src); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = &cmsgbuf.buf; + msg.msg_controllen = sizeof(cmsgbuf.buf); + + if ((r = recvmsg(fd, &msg, 0)) == -1) { + if (errno != EAGAIN && errno != EINTR) + log_debug("disc_recv_packet: read error: %s", + strerror(errno)); + return; + } + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == IPPROTO_IP && + cmsg->cmsg_type == IP_RECVIF) { + ifindex = ((struct sockaddr_dl *) + CMSG_DATA(cmsg))->sdl_index; + break; + } + } + + len = (u_int16_t)r; + + /* find a matching interface */ + if ((iface = find_iface(xconf, ifindex, src.sin_addr)) == NULL) { + log_debug("disc_recv_packet: cannot find a matching interface"); + return; + } + + /* LDP header sanity checks */ + if (len < LDP_HDR_SIZE || len > LDP_MAX_LEN) { + log_debug("disc_recv_packet: bad packet size"); + return; + } + ldp_hdr = (struct ldp_hdr *)buf; + + if (ntohs(ldp_hdr->version) != LDP_VERSION) { + log_debug("dsc_recv_packet: invalid LDP version %d", + ldp_hdr->version); + return; + } + + if (ntohs(ldp_hdr->length) > len || + len <= sizeof(struct ldp_hdr)) { + log_debug("disc_recv_packet: invalid LDP packet length %d", + ntohs(ldp_hdr->length)); + return; + } + + if ((l = ldp_hdr_sanity_check(ldp_hdr, len, iface)) == -1) + return; + + ldp_msg = (struct ldp_msg *)(buf + LDP_HDR_SIZE); + + if (len < LDP_MSG_LEN) { + log_debug("disc_recv_packet: invalid LDP packet length %d", + ntohs(ldp_hdr->length)); + return; + } + + /* switch LDP packet type */ + switch (ntohs(ldp_msg->type)) { + case MSG_TYPE_HELLO: + recv_hello(iface, src.sin_addr, buf, len); + break; + default: + log_debug("recv_packet: unknown LDP packet type, interface %s", + iface->name); + } +} + +int +ldp_hdr_sanity_check(struct ldp_hdr *ldp_hdr, u_int16_t len, + const struct iface *iface) +{ + struct in_addr addr; + + if (iface->type != IF_TYPE_VIRTUALLINK) { + if (ldp_hdr->lspace_id != iface->lspace_id) { + addr.s_addr = ldp_hdr->lspace_id; + log_debug("ldp_hdr_sanity_check: invalid label space " + "ID %s, interface %s", inet_ntoa(addr), + iface->name); + return (-1); + } + } else { + if (ldp_hdr->lspace_id != 0) { + addr.s_addr = ldp_hdr->lspace_id; + log_debug("ldp_hdr_sanity_check: invalid label space " + "ID %s, interface %s", inet_ntoa(addr), + iface->name); + return (-1); + } + } + + return (ntohs(ldp_hdr->length)); +} + +struct iface * +find_iface(struct ldpd_conf *xconf, unsigned int ifindex, struct in_addr src) +{ + struct iface *iface = NULL; + + /* returned interface needs to be active */ + LIST_FOREACH(iface, &xconf->iface_list, entry) { + switch (iface->type) { + case IF_TYPE_VIRTUALLINK: + if ((src.s_addr == iface->dst.s_addr) && + !iface->passive) + return (iface); + break; + case IF_TYPE_POINTOPOINT: + if (ifindex == iface->ifindex && + iface->dst.s_addr == src.s_addr && + !iface->passive) + return (iface); + break; + default: + if (ifindex == iface->ifindex && + (iface->addr.s_addr & iface->mask.s_addr) == + (src.s_addr & iface->mask.s_addr) && + !iface->passive) + return (iface); + break; + } + } + + return (NULL); +} + +void +session_recv_packet(int fd, short event, void *bula) +{ + struct sockaddr_in src; + struct ldpd_conf *xconf = bula; + struct iface *iface; + struct nbr *nbr = NULL; + int newfd, len; + + if (event != EV_READ) + return; + + newfd = accept(fd, (struct sockaddr *)&src, &len); + if (newfd == -1) { + log_debug("sess_recv_packet: accept error: %s", + strerror(errno)); + return; + } + + if (fcntl(newfd, F_SETFL, O_NONBLOCK) == -1) { + log_debug("sess_recv_packet: unable to set non blocking flag"); + return; + } + + if ((iface = session_find_iface(xconf, src.sin_addr)) == NULL) { + log_debug("sess_recv_packet: cannot find a matching interface"); + return; + } + + /* XXX */ + nbr = nbr_find_ip(iface, src.sin_addr.s_addr); + if (nbr == NULL) { + /* If there is no neighbor matching there is no + Hello adjacency: send notification */ + send_notification(S_NO_HELLO, iface, newfd); + close(newfd); + return; + } + + nbr->fd = newfd; + nbr_fsm(nbr, NBR_EVT_SESSION_UP); +} + +void +session_read(struct bufferevent *bev, void *arg) +{ + struct nbr *nbr = (struct nbr *)arg; + struct iface *iface = nbr->iface; + struct ldp_hdr *ldp_hdr; + struct ldp_msg *ldp_msg; + u_int16_t len = EVBUFFER_LENGTH(EVBUFFER_INPUT(bev)); + u_int16_t pdu_len; + char buffer[LDP_MAX_LEN]; + char *buf = buffer; + int l, msg_size = 0; + + bufferevent_read(bev, buf, len); + +another_packet: + ldp_hdr = (struct ldp_hdr *)buf; + + if (ntohs(ldp_hdr->version) != LDP_VERSION) { + log_debug("session_read: nbr ID %s invalid LDP version %d", + inet_ntoa(nbr->id), ldp_hdr->version); + send_notification_nbr(nbr, S_BAD_PROTO_VER); + session_close(nbr); + /* XXX: notify lde */ + return; + } + + pdu_len = ntohs(ldp_hdr->length); + + if (pdu_len < LDP_HDR_SIZE || pdu_len > LDP_MAX_LEN) { + log_debug("session_read: packet malformed"); + send_notification_nbr(nbr, S_BAD_MSG_LEN); + session_close(nbr); + /* XXX: notify lde */ + return; + } + + if ((l = ldp_hdr_sanity_check(ldp_hdr, len, iface)) == -1) + return; + + buf += LDP_HDR_SIZE; + len -= LDP_HDR_SIZE; + + pdu_len -= LDP_HDR_SIZE - PDU_HDR_SIZE; + + while (pdu_len > LDP_MSG_LEN) { + ldp_msg = (struct ldp_msg *)buf; + + /* switch LDP packet type */ + switch (ntohs(ldp_msg->type)) { + case MSG_TYPE_NOTIFICATION: + msg_size = recv_notification(nbr, buf, pdu_len); + break; + case MSG_TYPE_INIT: + msg_size = recv_init(nbr, buf, pdu_len); + break; + case MSG_TYPE_KEEPALIVE: + msg_size = recv_keepalive(nbr, buf, pdu_len); + break; + case MSG_TYPE_ADDR: + msg_size = recv_address(nbr, buf, pdu_len); + break; + case MSG_TYPE_ADDRWITHDRAW: + msg_size = recv_address_withdraw(nbr, buf, pdu_len); + break; + case MSG_TYPE_LABELMAPPING: + msg_size = recv_labelmapping(nbr, buf, pdu_len); + break; + case MSG_TYPE_LABELREQUEST: + msg_size = recv_labelrequest(nbr, buf, pdu_len); + break; + case MSG_TYPE_LABELWITHDRAW: + msg_size = recv_labelwithdraw(nbr, buf, pdu_len); + break; + case MSG_TYPE_LABELRELEASE: + msg_size = recv_labelrelease(nbr, buf, pdu_len); + break; + case MSG_TYPE_LABELABORTREQ: + case MSG_TYPE_HELLO: + default: + log_debug("session_read: unknown LDP packet type " + "interface %s", iface->name); + return; + } + + if (msg_size < 0) + return; + + /* Analyse the next message */ + buf += msg_size + TLV_HDR_LEN; + len -= msg_size + TLV_HDR_LEN; + pdu_len -= msg_size + TLV_HDR_LEN; + } + + if (len > LDP_HDR_SIZE) + goto another_packet; +} + +void +session_write(struct bufferevent *bev, void *arg) +{ +} + +void +session_error(struct bufferevent *bev, short what, void *arg) +{ + struct nbr *nbr = arg; + + nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION); +} + +void +session_close(struct nbr *nbr) +{ + log_debug("session_close: closing session with nbr ID %s", + inet_ntoa(nbr->id)); + + if (evtimer_pending(&nbr->keepalive_timer, NULL)) + evtimer_del(&nbr->keepalive_timer); + if (evtimer_pending(&nbr->keepalive_timeout, NULL)) + evtimer_del(&nbr->keepalive_timeout); + + bufferevent_free(nbr->bev); + close(nbr->fd); +} + +struct iface * +session_find_iface(struct ldpd_conf *xconf, struct in_addr src) +{ + struct iface *iface = NULL; + + /* returned interface needs to be active */ + LIST_FOREACH(iface, &xconf->iface_list, entry) { + switch (iface->type) { + case IF_TYPE_VIRTUALLINK: + if ((src.s_addr == iface->dst.s_addr) && + !iface->passive) + return (iface); + break; + case IF_TYPE_POINTOPOINT: + if (iface->dst.s_addr == src.s_addr && + !iface->passive) + return (iface); + break; + default: + if ((iface->addr.s_addr & iface->mask.s_addr) == + (src.s_addr & iface->mask.s_addr) && + !iface->passive) + return (iface); + break; + } + } + + return (NULL); +} diff --git a/usr.sbin/ldpd/parse.y b/usr.sbin/ldpd/parse.y new file mode 100644 index 00000000000..534e59a0dcf --- /dev/null +++ b/usr.sbin/ldpd/parse.y @@ -0,0 +1,888 @@ +/* $OpenBSD: parse.y,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org> + * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org> + * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. + * Copyright (c) 2001 Theo de Raadt. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +%{ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <unistd.h> +#include <ifaddrs.h> +#include <limits.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + +#include "ldp.h" +#include "ldpd.h" +#include "ldpe.h" +#include "log.h" + +TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); +static struct file { + TAILQ_ENTRY(file) entry; + FILE *stream; + char *name; + int lineno; + int errors; +} *file, *topfile; +struct file *pushfile(const char *, int); +int popfile(void); +int check_file_secrecy(int, const char *); +int yyparse(void); +int yylex(void); +int yyerror(const char *, ...); +int kw_cmp(const void *, const void *); +int lookup(char *); +int lgetc(int); +int lungetc(int); +int findeol(void); + +TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); +struct sym { + TAILQ_ENTRY(sym) entry; + int used; + int persist; + char *nam; + char *val; +}; + +int symset(const char *, const char *, int); +char *symget(const char *); + +void clear_config(struct ldpd_conf *xconf); +u_int32_t get_rtr_id(void); +int host(const char *, struct in_addr *, struct in_addr *); + +static struct ldpd_conf *conf; +static int errors = 0; + +struct iface *iface = NULL; + +struct config_defaults { + u_int16_t holdtime; + u_int16_t keepalive; + u_int16_t hello_interval; + u_int8_t mode; +}; + +struct config_defaults globaldefs; +struct config_defaults lspacedefs; +struct config_defaults ifacedefs; +struct config_defaults *defs; + +struct iface *conf_get_if(struct kif *, struct kif_addr *); + +typedef struct { + union { + int64_t number; + char *string; + } v; + int lineno; +} YYSTYPE; + +%} + +%token LSPACE INTERFACE ROUTERID LFIBUPDATE RTLABEL +%token HOLDTIME HELLOINTERVAL KEEPALIVE +%token DISTRIBUTION RETENTION ADVERTISEMENT +%token EXTTAG PASSIVE +%token HELLOINTERVAL +%token YES NO +%token ERROR +%token <v.string> STRING +%token <v.number> NUMBER +%type <v.number> yesno +%type <v.string> string + +%% + +grammar : /* empty */ + | grammar '\n' + | grammar conf_main '\n' + | grammar varset '\n' + | grammar interface '\n' + | grammar error '\n' { file->errors++; } + ; + +string : string STRING { + if (asprintf(&$$, "%s %s", $1, $2) == -1) { + free($1); + free($2); + yyerror("string: asprintf"); + YYERROR; + } + free($1); + free($2); + } + | STRING + ; + +yesno : YES { $$ = 1; } + | NO { $$ = 0; } + ; + +varset : STRING '=' string { + if (conf->opts & LDPD_OPT_VERBOSE) + printf("%s = \"%s\"\n", $1, $3); + if (symset($1, $3, 0) == -1) + fatal("cannot store variable"); + free($1); + free($3); + } + ; + +conf_main : ROUTERID STRING { + if (!inet_aton($2, &conf->rtr_id)) { + yyerror("error parsing router-id"); + free($2); + YYERROR; + } + free($2); + } + | LFIBUPDATE yesno { + if ($2 == 0) + conf->flags |= LDPD_FLAG_NO_LFIB_UPDATE; + else + conf->flags &= ~LDPD_FLAG_NO_LFIB_UPDATE; + } + | DISTRIBUTION STRING { + conf->mode &= ~(MODE_DIST_INDEPENDENT | + MODE_DIST_ORDERED); + + if (!strcmp($2, "independent")) + conf->mode |= MODE_DIST_INDEPENDENT; + else if (!strcmp($2, "ordered")) + conf->mode |= MODE_DIST_ORDERED; + else { + yyerror("unknown distribution type"); + free($2); + YYERROR; + } + } + | RETENTION STRING { + conf->mode &= ~(MODE_RET_CONSERVATIVE | + MODE_RET_LIBERAL); + + if (!strcmp($2, "conservative")) + conf->mode |= MODE_RET_CONSERVATIVE; + else if (!strcmp($2, "liberal")) + conf->mode |= MODE_RET_LIBERAL; + else { + yyerror("unknown retention type"); + free($2); + YYERROR; + } + } + | ADVERTISEMENT STRING { + conf->mode &= ~(MODE_ADV_ONDEMAND | + MODE_ADV_UNSOLICITED); + + if (!strcmp($2, "ondemand")) + conf->mode |= MODE_ADV_ONDEMAND; + else if (!strcmp($2, "unsolicited")) + conf->mode |= MODE_ADV_UNSOLICITED; + else { + yyerror("unknown retention type"); + free($2); + YYERROR; + } + } + | RTLABEL STRING EXTTAG NUMBER { + if ($4 < 0 || $4 > UINT_MAX) { + yyerror("invalid external route tag"); + free($2); + YYERROR; + } + rtlabel_tag(rtlabel_name2id($2), $4); + free($2); + } + | defaults + ; +defaults : HOLDTIME NUMBER { + if ($2 < MIN_HOLDTIME || + $2 > MAX_HOLDTIME) { + yyerror("holdtime out of range (%d-%d)", + MIN_HOLDTIME, MAX_HOLDTIME); + YYERROR; + } + defs->holdtime = $2; + } + | KEEPALIVE NUMBER { + if ($2 < MIN_KEEPALIVE || + $2 > MAX_KEEPALIVE) { + yyerror("keepalive out of range (%d-%d)", + MIN_KEEPALIVE, MAX_KEEPALIVE); + YYERROR; + } + defs->keepalive = $2; + } + | HELLOINTERVAL NUMBER { + if ($2 < MIN_HELLO_INTERVAL || + $2 > MAX_HELLO_INTERVAL) { + yyerror("hello-interval out of range (%d-%d)", + MIN_HELLO_INTERVAL, MAX_HELLO_INTERVAL); + YYERROR; + } + defs->hello_interval = $2; + } + ; + +optnl : '\n' optnl + | + ; + +nl : '\n' optnl /* one newline or more */ + ; + +interface : INTERFACE STRING { + struct kif *kif; + struct kif_addr *ka = NULL; + char *s; + struct in_addr addr; + + s = strchr($2, ':'); + if (s) { + *s++ = '\0'; + if (inet_aton(s, &addr) == 0) { + yyerror( + "error parsing interface address"); + free($2); + YYERROR; + } + } else + addr.s_addr = 0; + + if ((kif = kif_findname($2, addr, &ka)) == NULL) { + yyerror("unknown interface %s", $2); + free($2); + YYERROR; + } + if (ka == NULL) { + if (s) + yyerror("address %s not configured on " + "interface %s", s, $2); + else + yyerror("unnumbered interface %s", $2); + free($2); + YYERROR; + } + free($2); + iface = conf_get_if(kif, ka); + if (iface == NULL) + YYERROR; + LIST_INSERT_HEAD(&conf->iface_list, iface, entry); + + memcpy(&ifacedefs, defs, sizeof(ifacedefs)); + defs = &ifacedefs; + } interface_block { + iface->holdtime = defs->holdtime; + iface->keepalive = defs->keepalive; + iface->hello_interval = defs->hello_interval; + iface = NULL; + + defs = &globaldefs; + } + ; + +interface_block : '{' optnl interfaceopts_l '}' + | '{' optnl '}' + ; + +interfaceopts_l : interfaceopts_l interfaceoptsl nl + | interfaceoptsl optnl + ; + +interfaceoptsl : PASSIVE { iface->passive = 1; } + | defaults + ; + +%% + +struct keywords { + const char *k_name; + int k_val; +}; + +int +yyerror(const char *fmt, ...) +{ + va_list ap; + + file->errors++; + va_start(ap, fmt); + fprintf(stderr, "%s:%d: ", file->name, yylval.lineno); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + return (0); +} + +int +kw_cmp(const void *k, const void *e) +{ + return (strcmp(k, ((const struct keywords *)e)->k_name)); +} + +int +lookup(char *s) +{ + /* this has to be sorted always */ + static const struct keywords keywords[] = { + {"advertisement", ADVERTISEMENT}, + {"distribution", DISTRIBUTION}, + {"external-tag", EXTTAG}, + {"hello-interval", HELLOINTERVAL}, + {"holdtime", HOLDTIME}, + {"interface", INTERFACE}, + {"keepalive", KEEPALIVE}, + {"labelspace", LSPACE}, + {"lfib-update", LFIBUPDATE}, + {"passive", PASSIVE}, + {"retention", RETENTION}, + {"router-id", ROUTERID}, + {"rtlabel", RTLABEL}, + {"yes", YES} + }; + const struct keywords *p; + + p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), + sizeof(keywords[0]), kw_cmp); + + if (p) + return (p->k_val); + else + return (STRING); +} + +#define MAXPUSHBACK 128 + +char *parsebuf; +int parseindex; +char pushback_buffer[MAXPUSHBACK]; +int pushback_index = 0; + +int +lgetc(int quotec) +{ + int c, next; + + if (parsebuf) { + /* Read character from the parsebuffer instead of input. */ + if (parseindex >= 0) { + c = parsebuf[parseindex++]; + if (c != '\0') + return (c); + parsebuf = NULL; + } else + parseindex++; + } + + if (pushback_index) + return (pushback_buffer[--pushback_index]); + + if (quotec) { + if ((c = getc(file->stream)) == EOF) { + yyerror("reached end of file while parsing " + "quoted string"); + if (file == topfile || popfile() == EOF) + return (EOF); + return (quotec); + } + return (c); + } + + while ((c = getc(file->stream)) == '\\') { + next = getc(file->stream); + if (next != '\n') { + c = next; + break; + } + yylval.lineno = file->lineno; + file->lineno++; + } + + while (c == EOF) { + if (file == topfile || popfile() == EOF) + return (EOF); + c = getc(file->stream); + } + return (c); +} + +int +lungetc(int c) +{ + if (c == EOF) + return (EOF); + if (parsebuf) { + parseindex--; + if (parseindex >= 0) + return (c); + } + if (pushback_index < MAXPUSHBACK-1) + return (pushback_buffer[pushback_index++] = c); + else + return (EOF); +} + +int +findeol(void) +{ + int c; + + parsebuf = NULL; + pushback_index = 0; + + /* skip to either EOF or the first real EOL */ + while (1) { + c = lgetc(0); + if (c == '\n') { + file->lineno++; + break; + } + if (c == EOF) + break; + } + return (ERROR); +} + +int +yylex(void) +{ + char buf[8096]; + char *p, *val; + int quotec, next, c; + int token; + +top: + p = buf; + while ((c = lgetc(0)) == ' ' || c == '\t') + ; /* nothing */ + + yylval.lineno = file->lineno; + if (c == '#') + while ((c = lgetc(0)) != '\n' && c != EOF) + ; /* nothing */ + if (c == '$' && parsebuf == NULL) { + while (1) { + if ((c = lgetc(0)) == EOF) + return (0); + + if (p + 1 >= buf + sizeof(buf) - 1) { + yyerror("string too long"); + return (findeol()); + } + if (isalnum(c) || c == '_') { + *p++ = (char)c; + continue; + } + *p = '\0'; + lungetc(c); + break; + } + val = symget(buf); + if (val == NULL) { + yyerror("macro '%s' not defined", buf); + return (findeol()); + } + parsebuf = val; + parseindex = 0; + goto top; + } + + switch (c) { + case '\'': + case '"': + quotec = c; + while (1) { + if ((c = lgetc(quotec)) == EOF) + return (0); + if (c == '\n') { + file->lineno++; + continue; + } else if (c == '\\') { + if ((next = lgetc(quotec)) == EOF) + return (0); + if (next == quotec || c == ' ' || c == '\t') + c = next; + else if (next == '\n') + continue; + else + lungetc(next); + } else if (c == quotec) { + *p = '\0'; + break; + } + if (p + 1 >= buf + sizeof(buf) - 1) { + yyerror("string too long"); + return (findeol()); + } + *p++ = (char)c; + } + yylval.v.string = strdup(buf); + if (yylval.v.string == NULL) + err(1, "yylex: strdup"); + return (STRING); + } + +#define allowed_to_end_number(x) \ + (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') + + if (c == '-' || isdigit(c)) { + do { + *p++ = c; + if ((unsigned)(p-buf) >= sizeof(buf)) { + yyerror("string too long"); + return (findeol()); + } + } while ((c = lgetc(0)) != EOF && isdigit(c)); + lungetc(c); + if (p == buf + 1 && buf[0] == '-') + goto nodigits; + if (c == EOF || allowed_to_end_number(c)) { + const char *errstr = NULL; + + *p = '\0'; + yylval.v.number = strtonum(buf, LLONG_MIN, + LLONG_MAX, &errstr); + if (errstr) { + yyerror("\"%s\" invalid number: %s", + buf, errstr); + return (findeol()); + } + return (NUMBER); + } else { +nodigits: + while (p > buf + 1) + lungetc(*--p); + c = *--p; + if (c == '-') + return (c); + } + } + +#define allowed_in_string(x) \ + (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ + x != '{' && x != '}' && \ + x != '!' && x != '=' && x != '#' && \ + x != ',')) + + if (isalnum(c) || c == ':' || c == '_') { + do { + *p++ = c; + if ((unsigned)(p-buf) >= sizeof(buf)) { + yyerror("string too long"); + return (findeol()); + } + } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); + lungetc(c); + *p = '\0'; + if ((token = lookup(buf)) == STRING) + if ((yylval.v.string = strdup(buf)) == NULL) + err(1, "yylex: strdup"); + return (token); + } + if (c == '\n') { + yylval.lineno = file->lineno; + file->lineno++; + } + if (c == EOF) + return (0); + return (c); +} + +int +check_file_secrecy(int fd, const char *fname) +{ + struct stat st; + + if (fstat(fd, &st)) { + log_warn("cannot stat %s", fname); + return (-1); + } + if (st.st_uid != 0 && st.st_uid != getuid()) { + log_warnx("%s: owner not root or current user", fname); + return (-1); + } + if (st.st_mode & (S_IRWXG | S_IRWXO)) { + log_warnx("%s: group/world readable/writeable", fname); + return (-1); + } + return (0); +} + +struct file * +pushfile(const char *name, int secret) +{ + struct file *nfile; + + if ((nfile = calloc(1, sizeof(struct file))) == NULL || + (nfile->name = strdup(name)) == NULL) { + log_warn("malloc"); + return (NULL); + } + if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { + log_warn("%s", nfile->name); + free(nfile->name); + free(nfile); + return (NULL); + } else if (secret && + check_file_secrecy(fileno(nfile->stream), nfile->name)) { + fclose(nfile->stream); + free(nfile->name); + free(nfile); + return (NULL); + } + nfile->lineno = 1; + TAILQ_INSERT_TAIL(&files, nfile, entry); + return (nfile); +} + +int +popfile(void) +{ + struct file *prev; + + if ((prev = TAILQ_PREV(file, files, entry)) != NULL) + prev->errors += file->errors; + + TAILQ_REMOVE(&files, file, entry); + fclose(file->stream); + free(file->name); + free(file); + file = prev; + return (file ? 0 : EOF); +} + +struct ldpd_conf * +parse_config(char *filename, int opts) +{ + struct sym *sym, *next; + + if ((conf = calloc(1, sizeof(struct ldpd_conf))) == NULL) + fatal("parse_config"); + conf->opts = opts; + + bzero(&globaldefs, sizeof(globaldefs)); + defs = &globaldefs; + defs->holdtime = DEFAULT_HOLDTIME; + defs->keepalive = DEFAULT_KEEPALIVE; + defs->hello_interval = DEFAULT_HELLO_INTERVAL; + defs->mode = (MODE_DIST_INDEPENDENT | MODE_RET_LIBERAL | + MODE_ADV_UNSOLICITED); + + if ((file = pushfile(filename, !(conf->opts & LDPD_OPT_NOACTION))) == NULL) { + free(conf); + return (NULL); + } + topfile = file; + + yyparse(); + errors = file->errors; + popfile(); + + /* Free macros and check which have not been used. */ + for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) { + next = TAILQ_NEXT(sym, entry); + if ((conf->opts & LDPD_OPT_VERBOSE2) && !sym->used) + fprintf(stderr, "warning: macro '%s' not " + "used\n", sym->nam); + if (!sym->persist) { + free(sym->nam); + free(sym->val); + TAILQ_REMOVE(&symhead, sym, entry); + free(sym); + } + } + + /* free global config defaults */ + if (errors) { + clear_config(conf); + return (NULL); + } + + if (conf->rtr_id.s_addr == 0) + conf->rtr_id.s_addr = get_rtr_id(); + + return (conf); +} + +int +symset(const char *nam, const char *val, int persist) +{ + struct sym *sym; + + for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam); + sym = TAILQ_NEXT(sym, entry)) + ; /* nothing */ + + if (sym != NULL) { + if (sym->persist == 1) + return (0); + else { + free(sym->nam); + free(sym->val); + TAILQ_REMOVE(&symhead, sym, entry); + free(sym); + } + } + if ((sym = calloc(1, sizeof(*sym))) == NULL) + return (-1); + + sym->nam = strdup(nam); + if (sym->nam == NULL) { + free(sym); + return (-1); + } + sym->val = strdup(val); + if (sym->val == NULL) { + free(sym->nam); + free(sym); + return (-1); + } + sym->used = 0; + sym->persist = persist; + TAILQ_INSERT_TAIL(&symhead, sym, entry); + return (0); +} + +int +cmdline_symset(char *s) +{ + char *sym, *val; + int ret; + size_t len; + + if ((val = strrchr(s, '=')) == NULL) + return (-1); + + len = strlen(s) - strlen(val) + 1; + if ((sym = malloc(len)) == NULL) + errx(1, "cmdline_symset: malloc"); + + strlcpy(sym, s, len); + + ret = symset(sym, val + 1, 1); + free(sym); + + return (ret); +} + +char * +symget(const char *nam) +{ + struct sym *sym; + + TAILQ_FOREACH(sym, &symhead, entry) + if (strcmp(nam, sym->nam) == 0) { + sym->used = 1; + return (sym->val); + } + return (NULL); +} + +struct iface * +conf_get_if(struct kif *kif, struct kif_addr *ka) +{ + struct iface *i; + + LIST_FOREACH(i, &conf->iface_list, entry) { + if (i->ifindex == kif->ifindex && + i->addr.s_addr == ka->addr.s_addr) { + yyerror("interface %s already configured", + kif->ifname); + return (NULL); + } + } + + i = if_new(kif, ka); + + return (i); +} + +void +clear_config(struct ldpd_conf *xconf) +{ + struct iface *i; + + while ((i = LIST_FIRST(&conf->iface_list)) != NULL) { + LIST_REMOVE(i, entry); + if_del(i); + } + + free(xconf); +} + +u_int32_t +get_rtr_id(void) +{ + struct ifaddrs *ifap, *ifa; + u_int32_t ip = 0, cur, localnet; + + localnet = htonl(INADDR_LOOPBACK & IN_CLASSA_NET); + + if (getifaddrs(&ifap) == -1) + fatal("getifaddrs"); + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family != AF_INET) + continue; + cur = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr; + if ((cur & localnet) == localnet) /* skip 127/8 */ + continue; + if (cur > ip || ip == 0) + ip = cur; + } + freeifaddrs(ifap); + + if (ip == 0) + fatal("router-id is 0.0.0.0"); + + return (ip); +} + +int +host(const char *s, struct in_addr *addr, struct in_addr *mask) +{ + struct in_addr ina; + int bits = 32; + + bzero(&ina, sizeof(struct in_addr)); + if (strrchr(s, '/') != NULL) { + if ((bits = inet_net_pton(AF_INET, s, &ina, sizeof(ina))) == -1) + return (0); + } else { + if (inet_pton(AF_INET, s, &ina) != 1) + return (0); + } + + addr->s_addr = ina.s_addr; + mask->s_addr = prefixlen2mask(bits); + + return (1); +} diff --git a/usr.sbin/ldpd/printconf.c b/usr.sbin/ldpd/printconf.c new file mode 100644 index 00000000000..58bca2b7410 --- /dev/null +++ b/usr.sbin/ldpd/printconf.c @@ -0,0 +1,94 @@ +/* $OpenBSD: printconf.c,v 1.1 2009/06/01 20:59:45 michele Exp $ */ + +/* + * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/queue.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <stdio.h> + +#include "ldp.h" +#include "ldpd.h" +#include "ldpe.h" + +void print_mainconf(struct ldpd_conf *); +void print_rtlabel(struct ldpd_conf *); +void print_iface(struct iface *); + +void +print_mainconf(struct ldpd_conf *conf) +{ + printf("router-id %s\n\n", inet_ntoa(conf->rtr_id)); + + if (conf->mode & MODE_DIST_INDEPENDENT) + printf("Distribution: Independent\n"); + else + printf("Distribution: Ordered\n"); + + if (conf->mode & MODE_RET_LIBERAL) + printf("Retention: Liberal\n"); + else + printf("Retention: Conservative\n"); + + if (conf->mode & MODE_ADV_ONDEMAND) + printf("Advertisement: On demand\n"); + else + printf("Advertisement: Unsolicited\n"); +} + +void +print_rtlabel(struct ldpd_conf *conf) +{ + struct n2id_label *label; + + TAILQ_FOREACH(label, &rt_labels, entry) + if (label->ext_tag) + printf("rtlabel \"%s\" external-tag %u\n", + label->name, label->ext_tag); +} + +void +print_iface(struct iface *iface) +{ + + printf("\tinterface %s: %s {\n", iface->name, inet_ntoa(iface->addr)); + + printf("\t\tholdtime %d\n", iface->holdtime); + printf("\t\thello-interval %d\n", iface->hello_interval); + + if (iface->passive) + printf("\t\tpassive\n"); + + printf("\t}\n"); +} + +void +print_config(struct ldpd_conf *conf) +{ + struct iface *iface; + + printf("\n"); + print_mainconf(conf); + printf("\n"); + + LIST_FOREACH(iface, &conf->iface_list, entry) { + print_iface(iface); + } +} |