Index: usr.sbin/ldpd/hello.c =================================================================== RCS file: /cvs/src/usr.sbin/ldpd/hello.c,v retrieving revision 1.12 diff -u -p -u -p -r1.12 hello.c --- usr.sbin/ldpd/hello.c 12 Mar 2011 01:57:13 -0000 1.12 +++ usr.sbin/ldpd/hello.c 3 Dec 2012 11:57:57 -0000 @@ -42,7 +42,7 @@ int tlv_decode_opt_hello_prms(char *, u_ int gen_hello_prms_tlv(struct iface *, struct ibuf *, u_int16_t); int -send_hello(struct iface *iface) +send_hello(struct iface *iface, struct in_addr *na) { struct sockaddr_in dst; struct ibuf *buf; @@ -51,7 +51,10 @@ send_hello(struct iface *iface) 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 (na == NULL) + inet_aton(AllRouters, &dst.sin_addr); + else + dst.sin_addr.s_addr = na->s_addr; if (iface->passive) return (0); @@ -173,7 +176,10 @@ gen_hello_prms_tlv(struct iface *iface, parms.length = htons(size); /* XXX */ parms.holdtime = htons(iface->holdtime); - parms.flags = 0; + if ((iface->flags & htons(TARGETED_HELLO)) != 0) + parms.flags = htons(TARGETED_HELLO | REQUEST_TARG_HELLO); + else + parms.flags = 0; return (ibuf_add(buf, &parms, sizeof(parms))); } Index: usr.sbin/ldpd/interface.c =================================================================== RCS file: /cvs/src/usr.sbin/ldpd/interface.c,v retrieving revision 1.9 diff -u -p -u -p -r1.9 interface.c --- usr.sbin/ldpd/interface.c 14 May 2012 10:17:21 -0000 1.9 +++ usr.sbin/ldpd/interface.c 3 Dec 2012 11:57:57 -0000 @@ -194,7 +194,7 @@ if_hello_timer(int fd, short event, void struct iface *iface = arg; struct timeval tv; - send_hello(iface); + send_hello(iface, NULL); /* reschedule hello_timer */ timerclear(&tv); @@ -474,3 +474,18 @@ if_set_mcast_loop(int fd) return (0); } + +int +if_set_recv_dstaddr(int fd) +{ + int recvaddr = 1; + + if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, + &recvaddr, sizeof(recvaddr)) < 0) { + log_warn("if_set_rcv_dstaddr: error setting IP_RECVDSTADDR"); + return (-1); + } + + return (0); +} + Index: usr.sbin/ldpd/ldpd.h =================================================================== RCS file: /cvs/src/usr.sbin/ldpd/ldpd.h,v retrieving revision 1.29 diff -u -p -u -p -r1.29 ldpd.h --- usr.sbin/ldpd/ldpd.h 12 Apr 2012 17:33:43 -0000 1.29 +++ usr.sbin/ldpd/ldpd.h 3 Dec 2012 11:57:57 -0000 @@ -158,7 +158,8 @@ enum nbr_event { NBR_EVT_KEEPALIVE_RCVD, NBR_EVT_PDU_RCVD, NBR_EVT_INIT_SENT, - NBR_EVT_DOWN + NBR_EVT_DOWN, + NBR_EVT_HTIMER_EXPRY }; /* neighbor actions */ @@ -169,6 +170,7 @@ enum nbr_action { NBR_ACT_RST_KTIMEOUT, NBR_ACT_STRT_KTIMER, NBR_ACT_RST_KTIMER, + NBR_ACT_STRT_HTIMER, NBR_ACT_SESSION_EST, NBR_ACT_INIT_SEND, NBR_ACT_KEEPALIVE_SEND, @@ -226,6 +228,17 @@ struct iface { u_int8_t passive; }; +struct target_nbr { + LIST_ENTRY(target_nbr) entry; + struct in_addr addr; + u_int16_t lspace; + u_int16_t holdtime; + u_int16_t keepalive; + u_int8_t priority; + u_int8_t options; + u_int32_t ifindex; +}; + /* ldp_conf */ enum { PROC_MAIN, @@ -249,6 +262,7 @@ struct ldpd_conf { struct event disc_ev; struct in_addr rtr_id; LIST_HEAD(, iface) iface_list; + LIST_HEAD(, target_nbr) tnbr_list; u_int32_t opts; #define LDPD_OPT_VERBOSE 0x00000001 Index: usr.sbin/ldpd/ldpe.c =================================================================== RCS file: /cvs/src/usr.sbin/ldpd/ldpe.c,v retrieving revision 1.16 diff -u -p -u -p -r1.16 ldpe.c --- usr.sbin/ldpd/ldpe.c 12 Apr 2012 17:33:43 -0000 1.16 +++ usr.sbin/ldpd/ldpe.c 3 Dec 2012 11:57:57 -0000 @@ -71,7 +71,9 @@ pid_t ldpe(struct ldpd_conf *xconf, int pipe_parent2ldpe[2], int pipe_ldpe2lde[2], int pipe_parent2lde[2]) { - struct iface *iface; + struct iface *iface, *nbrif; + struct nbr *nbr; + struct target_nbr *tnbr; struct passwd *pw; struct event ev_sigint, ev_sigterm; struct sockaddr_in disc_addr, sess_addr; @@ -114,6 +116,8 @@ ldpe(struct ldpd_conf *xconf, int pipe_p fatal("if_set_tos"); if (if_set_recvif(xconf->ldp_discovery_socket, 1) == -1) fatal("if_set_recvif"); + if (if_set_recv_dstaddr(xconf->ldp_discovery_socket) == -1) + fatal("if_set_recv_dstaddr"); if_set_recvbuf(xconf->ldp_discovery_socket); /* create the session TCP socket */ @@ -214,6 +218,21 @@ ldpe(struct ldpd_conf *xconf, int pipe_p log_debug("error starting interface %s", iface->name); } + } + + LIST_FOREACH(tnbr, &leconf->tnbr_list, entry) { + /* XXX: FIXME */ + LIST_FOREACH(nbrif, &leconf->iface_list, entry) + if (nbrif->ifindex == tnbr->ifindex) + break; + if (nbrif == NULL) + fatal("ldpe: could not find nbr iface"); + nbr = nbr_new(tnbr->addr.s_addr, tnbr->lspace, nbrif, + tnbr->addr); + nbr->options = tnbr->options; + nbr->hello_type = htons(TARGETED_HELLO); + nbr->holdtime = TARGETED_DFLT_HOLDTIME; + nbr_fsm(nbr, NBR_EVT_HTIMER_EXPRY); } event_dispatch(); Index: usr.sbin/ldpd/ldpe.h =================================================================== RCS file: /cvs/src/usr.sbin/ldpd/ldpe.h,v retrieving revision 1.16 diff -u -p -u -p -r1.16 ldpe.h --- usr.sbin/ldpd/ldpe.h 12 Apr 2012 17:33:43 -0000 1.16 +++ usr.sbin/ldpd/ldpe.h 3 Dec 2012 11:57:57 -0000 @@ -42,6 +42,7 @@ struct nbr { struct event keepalive_timer; struct event keepalive_timeout; struct event initdelay_timer; + struct event hello_timer; struct mapping_head mapping_list; struct mapping_head withdraw_list; @@ -81,7 +82,7 @@ void accept_pause(void); void accept_unpause(void); /* hello.c */ -int send_hello(struct iface *); +int send_hello(struct iface *, struct in_addr *); void recv_hello(struct iface *, struct in_addr, char *, u_int16_t); /* init.c */ @@ -148,6 +149,7 @@ 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_recv_dstaddr(int); /* neighbor.c */ struct nbr *nbr_new(u_int32_t, u_int16_t, struct iface *, struct in_addr); @@ -157,6 +159,9 @@ struct nbr *nbr_find_ip(u_int32_t); struct nbr *nbr_find_ldpid(u_int32_t, u_int16_t); struct nbr *nbr_find_peerid(u_int32_t); +struct target_nbr *tnbr_new(struct in_addr *); +void tnbr_del(struct target_nbr *); + int nbr_fsm(struct nbr *, enum nbr_event); void nbr_itimer(int, short, void *); @@ -172,6 +177,10 @@ void nbr_idtimer(int, short, void *); void nbr_start_idtimer(struct nbr *); void nbr_stop_idtimer(struct nbr *); int nbr_pending_idtimer(struct nbr *); +void nbr_hello_timer(int, short, void *); +void nbr_start_hello_timer(struct nbr *); +void nbr_stop_hello_timer(struct nbr *); +int nbr_pending_hello_timer(struct nbr *nbr); int nbr_act_session_establish(struct nbr *, int); Index: usr.sbin/ldpd/neighbor.c =================================================================== RCS file: /cvs/src/usr.sbin/ldpd/neighbor.c,v retrieving revision 1.24 diff -u -p -u -p -r1.24 neighbor.c --- usr.sbin/ldpd/neighbor.c 12 Mar 2011 01:57:13 -0000 1.24 +++ usr.sbin/ldpd/neighbor.c 3 Dec 2012 11:57:57 -0000 @@ -97,6 +97,7 @@ struct { /* Discovery States */ {NBR_STA_DOWN, NBR_EVT_HELLO_RCVD, NBR_ACT_STRT_ITIMER, NBR_STA_PRESENT}, {NBR_STA_SESSION, NBR_EVT_HELLO_RCVD, NBR_ACT_RST_ITIMER, 0}, + {NBR_STA_DOWN, NBR_EVT_HTIMER_EXPRY, NBR_ACT_STRT_HTIMER, 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}, @@ -121,7 +122,8 @@ const char * const nbr_event_names[] = { "KEEPALIVE RECEIVED", "PDU RECEIVED", "INIT SENT", - "DOWN" + "DOWN", + "HELLO TIMER EXPIRED", }; const char * const nbr_action_names[] = { @@ -131,6 +133,7 @@ const char * const nbr_action_names[] = "RESET KEEPALIVE TIMEOUT", "START KEEPALIVE TIMER", "RESET KEEPALIVE TIMER", + "START HELLO TIMER", "START NEIGHBOR SESSION", "SEND INIT", "SEND KEEPALIVE", @@ -182,6 +185,11 @@ nbr_fsm(struct nbr *nbr, enum nbr_event send_address(nbr, NULL); nbr_send_labelmappings(nbr); break; + case NBR_ACT_STRT_HTIMER: + if (nbr_pending_hello_timer(nbr)) + nbr_stop_hello_timer(nbr); /* XXX: fatalx? */ + nbr_start_hello_timer(nbr); + break; case NBR_ACT_SESSION_EST: ret = nbr_act_session_establish(nbr, 0); break; @@ -275,6 +283,8 @@ nbr_new(u_int32_t nbr_id, u_int16_t lspa evtimer_set(&nbr->keepalive_timeout, nbr_ktimeout, nbr); evtimer_set(&nbr->keepalive_timer, nbr_ktimer, nbr); evtimer_set(&nbr->initdelay_timer, nbr_idtimer, nbr); + if (nbr->iface->state == IF_STA_LOOPBACK) + evtimer_set(&nbr->hello_timer, nbr_hello_timer, nbr); return (nbr); } @@ -292,6 +302,8 @@ nbr_del(struct nbr *nbr) evtimer_del(&nbr->keepalive_timeout); if (evtimer_pending(&nbr->initdelay_timer, NULL)) evtimer_del(&nbr->initdelay_timer); + if (evtimer_pending(&nbr->hello_timer, NULL)) + evtimer_del(&nbr->hello_timer); nbr_mapping_list_clr(nbr, &nbr->mapping_list); @@ -340,7 +352,10 @@ nbr_itimer(int fd, short event, void *ar log_debug("nbr_itimer: neighbor ID %s peerid %lu", inet_ntoa(nbr->id), nbr->peerid); - nbr_del(nbr); + if ((nbr->hello_type & ntohs(TARGETED_HELLO)) == 0) + nbr_del(nbr); + else + nbr_start_idtimer(nbr); } void @@ -477,6 +492,55 @@ nbr_pending_idtimer(struct nbr *nbr) return (0); } + +void +nbr_hello_timer(int fd, short event, void *arg) +{ + u_int8_t flags; + struct nbr *nbr = arg; + struct timeval tv; + + if (nbr->iface->state != IF_STA_LOOPBACK) { + /* XXX: ugly - overlaps with IFF_NOARP */ + flags = nbr->iface->flags; + nbr->iface->flags |= nbr->hello_type; + send_hello(nbr->iface, &nbr->id); + nbr->iface->flags = flags; + } + + timerclear(&tv); + tv.tv_sec = DEFAULT_HELLO_INTERVAL; + if (evtimer_add(&nbr->hello_timer, &tv) == -1) + fatal("nbr_hello_timer"); +} + +void +nbr_start_hello_timer(struct nbr *nbr) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = DEFAULT_HELLO_INTERVAL; + if (evtimer_add(&nbr->hello_timer, &tv) == -1) + fatal("nbr_start_hello_timer"); +} + +void +nbr_stop_hello_timer(struct nbr *nbr) +{ + if (evtimer_del(&nbr->hello_timer) == -1) + fatal("nbr_stop_hello_timer"); +} + +int +nbr_pending_hello_timer(struct nbr *nbr) +{ + if (evtimer_pending(&nbr->hello_timer, NULL)) + return (1); + + return (0); +} + int nbr_establish_connection(struct nbr *nbr) { @@ -642,3 +706,28 @@ ldpe_nbr_ctl(struct ctl_conn *c) } imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0); } + +struct target_nbr * +tnbr_new(struct in_addr *id) +{ + struct target_nbr *tnbr; + + if ((tnbr = calloc(1, sizeof(*tnbr))) == NULL) + err(1, "tnbr_new: calloc"); + + tnbr->addr.s_addr = id->s_addr; + tnbr->lspace = 0; + tnbr->holdtime = TARGETED_DFLT_HOLDTIME; + tnbr->keepalive = DEFAULT_KEEPALIVE; + tnbr->priority = 0; + tnbr->options = htons(TARGETED_HELLO | REQUEST_TARG_HELLO); + + return (tnbr); +} + +void +tnbr_del(struct target_nbr *tnbr) +{ + free(tnbr); +} + Index: usr.sbin/ldpd/packet.c =================================================================== RCS file: /cvs/src/usr.sbin/ldpd/packet.c,v retrieving revision 1.16 diff -u -p -u -p -r1.16 packet.c --- usr.sbin/ldpd/packet.c 12 Apr 2012 17:33:43 -0000 1.16 +++ usr.sbin/ldpd/packet.c 3 Dec 2012 11:57:57 -0000 @@ -111,13 +111,14 @@ disc_recv_packet(int fd, short event, vo struct cmsghdr hdr; char buf[CMSG_SPACE(sizeof(struct sockaddr_dl))]; } cmsgbuf; - struct sockaddr_in src; + struct sockaddr_in src, dst = { 0 }; struct msghdr msg; struct iovec iov; struct ldpd_conf *xconf = bula; struct ldp_hdr ldp_hdr; struct ldp_msg ldp_msg; struct iface *iface; + struct nbr *nbr; char *buf; struct cmsghdr *cmsg; ssize_t r; @@ -151,19 +152,40 @@ disc_recv_packet(int fd, short event, vo cmsg->cmsg_type == IP_RECVIF) { ifindex = ((struct sockaddr_dl *) CMSG_DATA(cmsg))->sdl_index; - break; + if (dst.sin_addr.s_addr != 0) + break; + } + if (cmsg->cmsg_level == IPPROTO_IP && + cmsg->cmsg_type == IP_RECVDSTADDR) { + memcpy(&dst.sin_addr, CMSG_DATA(cmsg), + sizeof(struct in_addr)); + if (ifindex != 0) + 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"); + nbr = nbr_find_ip(src.sin_addr.s_addr); + if (IN_MULTICAST(dst.sin_addr.s_addr)) { + /* 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; + } + /* discard link level hellos from targeted neighbors */ + if (nbr != NULL && (nbr->hello_type & ntohs(TARGETED_HELLO))) { + log_debug("disc_recv_packet: link level Hello from" + "targeted neighbor %s", inet_ntoa(nbr->id)); + return; + } + } else if (nbr == NULL || (iface = nbr->iface) == NULL) { + log_warn("discard PDU unknown nbr %s", inet_ntoa(src.sin_addr)); return; } - /* LDP header sanity checks */ + /* LDP header sanity checks */ if (len < LDP_HDR_SIZE || len > LDP_MAX_LEN) { log_debug("disc_recv_packet: bad packet size"); return; Index: usr.sbin/ldpd/parse.y =================================================================== RCS file: /cvs/src/usr.sbin/ldpd/parse.y,v retrieving revision 1.7 diff -u -p -u -p -r1.7 parse.y --- usr.sbin/ldpd/parse.y 1 Sep 2010 13:54:54 -0000 1.7 +++ usr.sbin/ldpd/parse.y 3 Dec 2012 11:57:57 -0000 @@ -95,7 +95,8 @@ struct config_defaults lspacedefs; struct config_defaults ifacedefs; struct config_defaults *defs; -struct iface *conf_get_if(struct kif *, struct kif_addr *); +struct iface *conf_get_if(struct kif *, struct kif_addr *); +struct target_nbr *conf_get_nbr(struct in_addr *); typedef struct { union { @@ -107,7 +108,7 @@ typedef struct { %} -%token LSPACE INTERFACE ROUTERID FIBUPDATE +%token LSPACE INTERFACE NEIGHBOR ROUTERID FIBUPDATE %token HOLDTIME HELLOINTERVAL KEEPALIVE %token DISTRIBUTION RETENTION ADVERTISEMENT %token EXTTAG PASSIVE @@ -126,6 +127,7 @@ grammar : /* empty */ | grammar conf_main '\n' | grammar varset '\n' | grammar interface '\n' + | grammar neighbor '\n' | grammar error '\n' { file->errors++; } ; @@ -313,6 +315,23 @@ interfaceoptsl : PASSIVE { iface->passi | defaults ; +neighbor : NEIGHBOR STRING { + struct target_nbr *nbr; + struct in_addr addr; + + if (inet_aton($2, &addr) == 0) { + yyerror("bad neighbor address %s", $2); + free($2); + YYERROR; + } + free($2); + nbr = conf_get_nbr(&addr); + if (nbr == NULL) + YYERROR; + LIST_INSERT_HEAD(&conf->tnbr_list, nbr, entry); + + } + ; %% struct keywords { @@ -352,6 +371,7 @@ lookup(char *s) {"hello-interval", HELLOINTERVAL}, {"holdtime", HOLDTIME}, {"interface", INTERFACE}, + {"neighbor", NEIGHBOR}, {"keepalive", KEEPALIVE}, {"labelspace", LSPACE}, {"passive", PASSIVE}, @@ -822,16 +842,53 @@ conf_get_if(struct kif *kif, struct kif_ return (i); } +struct target_nbr * +conf_get_nbr(struct in_addr *addr) +{ + static u_int32_t loindex = 0; + struct target_nbr *t; + struct iface *i; + + if (loindex == 0) { + LIST_FOREACH(i, &conf->iface_list, entry) + if (i->state == IF_STA_LOOPBACK) { + loindex = i->ifindex; + break; + } + if (loindex == 0) { + yyerror("targeted nbr requires loopback interface"); + return (NULL); + } + } + + LIST_FOREACH(t, &conf->tnbr_list, entry) + if (t->addr.s_addr == addr->s_addr) { + yyerror("targeted nbr %s already configured", + inet_ntoa(*addr)); + return (NULL); + } + + t = tnbr_new(addr); + t->ifindex = loindex; + + return (t); +} + void clear_config(struct ldpd_conf *xconf) { struct iface *i; + struct target_nbr *t; while ((i = LIST_FIRST(&conf->iface_list)) != NULL) { LIST_REMOVE(i, entry); if_del(i); } + while ((t = LIST_FIRST(&conf->tnbr_list)) != NULL) { + LIST_REMOVE(t, entry); + tnbr_del(t); + } free(xconf); } Index: usr.sbin/ldpd/printconf.c =================================================================== RCS file: /cvs/src/usr.sbin/ldpd/printconf.c,v retrieving revision 1.3 diff -u -p -u -p -r1.3 printconf.c --- usr.sbin/ldpd/printconf.c 25 May 2010 13:29:45 -0000 1.3 +++ usr.sbin/ldpd/printconf.c 3 Dec 2012 11:57:57 -0000 @@ -30,6 +30,7 @@ void print_mainconf(struct ldpd_conf *); void print_iface(struct iface *); +void print_tnbr(struct target_nbr *); void print_mainconf(struct ldpd_conf *conf) @@ -64,14 +65,25 @@ print_iface(struct iface *iface) } void +print_tnbr(struct target_nbr *tn) +{ + printf("\nneighbor %s\n", inet_ntoa(tn->addr)); +} + +void print_config(struct ldpd_conf *conf) { struct iface *iface; + struct target_nbr *tnbr; print_mainconf(conf); printf("\n"); LIST_FOREACH(iface, &conf->iface_list, entry) { print_iface(iface); + } + + LIST_FOREACH(tnbr, &conf->tnbr_list, entry) { + print_tnbr(tnbr); } }