/* $OpenBSD: message.c,v 1.9 2007/10/24 20:52:50 claudio Exp $ */ /* * Copyright (c) 2006 Michele Marchetto * * 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 #include #include #include #include #include #include #include #include #include "ripd.h" #include "rip.h" #include "ripe.h" #include "log.h" extern struct ripd_conf *oeconf; void delete_entry(struct rip_route *); /* timers */ /* ARGSUSED */ void report_timer(int fd, short event, void *arg) { struct timeval tv; ripe_imsg_compose_rde(IMSG_FULL_RESPONSE, 0, 0, NULL, 0); /* restart report timer */ timerclear(&tv); tv.tv_sec = KEEPALIVE + arc4random() % OFFSET; evtimer_add(&oeconf->report_timer, &tv); } int start_report_timer(void) { struct timeval tv; timerclear(&tv); tv.tv_sec = KEEPALIVE + arc4random() % OFFSET; return (evtimer_add(&oeconf->report_timer, &tv)); } /* list handlers */ void add_entry(struct packet_head *r_list, struct rip_route *rr) { struct packet_entry *re; if (rr == NULL) fatalx("add_entry: no route report"); if ((re = calloc(1, sizeof(*re))) == NULL) fatal("add_response"); TAILQ_INSERT_TAIL(r_list, re, entry); re->rr = rr; rr->refcount++; } void delete_entry(struct rip_route *rr) { if (--rr->refcount == 0) free(rr); } void clear_list(struct packet_head *r_list) { struct packet_entry *re; while ((re = TAILQ_FIRST(r_list)) != NULL) { TAILQ_REMOVE(r_list, re, entry); delete_entry(re->rr); free(re); } } /* communications */ int send_triggered_update(struct iface *iface, struct rip_route *rr) { struct sockaddr_in dst; struct buf *buf; u_int16_t afi, route_tag; u_int32_t address, netmask, nexthop, metric; inet_aton(ALL_RIP_ROUTERS, &dst.sin_addr); dst.sin_port = htons(RIP_PORT); dst.sin_family = AF_INET; dst.sin_len = sizeof(struct sockaddr_in); if (iface->passive) return (0); if ((buf = buf_open(iface->mtu - sizeof(struct ip) - sizeof(struct udphdr))) == NULL) fatal("send_triggered_update"); gen_rip_hdr(buf, COMMAND_RESPONSE); afi = htons(AF_INET); route_tag = 0; address = rr->address.s_addr; netmask = rr->mask.s_addr; nexthop = rr->nexthop.s_addr; metric = htonl(rr->metric); buf_add(buf, &afi, sizeof(afi)); buf_add(buf, &route_tag, sizeof(route_tag)); buf_add(buf, &address, sizeof(address)); buf_add(buf, &netmask, sizeof(netmask)); buf_add(buf, &nexthop, sizeof(nexthop)); buf_add(buf, &metric, sizeof(metric)); send_packet(iface, buf->buf, buf->wpos, &dst); buf_free(buf); return (0); } int send_request(struct packet_head *r_list, struct iface *i, struct nbr *nbr) { struct buf *buf; struct iface *iface; struct packet_entry *entry; struct sockaddr_in dst; u_int8_t nentries; u_int8_t single_entry = 0; u_int32_t address, netmask, nexthop; u_int16_t port, afi, route_tag; u_int32_t metric; if (i == NULL) { /* directly to a nbr */ iface = nbr->iface; dst.sin_addr = nbr->addr; port = htons(nbr->port); } else { /* multicast on interface */ iface = i; inet_aton(ALL_RIP_ROUTERS, &dst.sin_addr); port = htons(RIP_PORT); } dst.sin_port = port; dst.sin_family = AF_INET; dst.sin_len = sizeof(struct sockaddr_in); if (iface->passive) return (0); while (!TAILQ_EMPTY(r_list)) { if ((buf = buf_open(iface->mtu - sizeof(struct ip) - sizeof(struct udphdr))) == NULL) fatal("send_request"); gen_rip_hdr(buf, COMMAND_REQUEST); route_tag = 0; nentries = 0; if (TAILQ_FIRST(r_list) == TAILQ_LAST(r_list, packet_head)) single_entry = 1; while (((entry = TAILQ_FIRST(r_list)) != NULL) && nentries < MAX_RIP_ENTRIES) { afi = htons(AF_INET); address = entry->rr->address.s_addr; netmask = entry->rr->mask.s_addr; nexthop = entry->rr->nexthop.s_addr; metric = htonl(entry->rr->metric); if (metric == htonl(INFINITY) && single_entry) afi = AF_UNSPEC; buf_add(buf, &afi, sizeof(afi)); buf_add(buf, &route_tag, sizeof(route_tag)); buf_add(buf, &address, sizeof(address)); buf_add(buf, &netmask, sizeof(netmask)); buf_add(buf, &nexthop, sizeof(nexthop)); buf_add(buf, &metric, sizeof(metric)); TAILQ_REMOVE(r_list, entry, entry); delete_entry(entry->rr); free(entry); nentries++; } send_packet(iface, buf->buf, buf->wpos, &dst); buf_free(buf); } return (0); } int send_response(struct packet_head *r_list, struct iface *i, struct nbr *nbr) { struct buf *buf; struct iface *iface; struct packet_entry *entry; struct sockaddr_in dst; u_int8_t nentries; u_int16_t port, afi, route_tag; u_int32_t address, netmask, nexthop; u_int32_t metric; if (i == NULL) { /* directly to a nbr */ iface = nbr->iface; dst.sin_addr = nbr->addr; port = htons(nbr->port); } else { /* multicast on interface */ iface = i; inet_aton(ALL_RIP_ROUTERS, &dst.sin_addr); port = htons(RIP_PORT); } dst.sin_port = port; dst.sin_family = AF_INET; dst.sin_len = sizeof(struct sockaddr_in); if (iface->passive) return (0); while (!TAILQ_EMPTY(r_list)) { if ((buf = buf_open(iface->mtu - sizeof(struct ip) - sizeof(struct udphdr))) == NULL) fatal("send_response"); gen_rip_hdr(buf, COMMAND_RESPONSE); afi = htons(AF_INET); route_tag = 0; nentries = 0; if (iface->auth_type != AUTH_NONE) { if (auth_gen(buf, iface) == -1) { buf_free(buf); return (-1); } nentries++; } while ((entry = TAILQ_FIRST(r_list)) != NULL && nentries < MAX_RIP_ENTRIES) { address = entry->rr->address.s_addr; netmask = entry->rr->mask.s_addr; nexthop = entry->rr->nexthop.s_addr; metric = htonl(entry->rr->metric); if (entry->rr->ifindex == iface->ifindex) { if (oeconf->options & OPT_SPLIT_HORIZON) goto free; else if (oeconf->options & OPT_SPLIT_POISONED) metric = htonl(INFINITY); } /* If the nexthop is not reachable through the * outgoing interface set it to INADDR_ANY */ if ((nexthop & iface->mask.s_addr) != (iface->addr.s_addr & iface->mask.s_addr)) nexthop = INADDR_ANY; buf_add(buf, &afi, sizeof(afi)); buf_add(buf, &route_tag, sizeof(route_tag)); buf_add(buf, &address, sizeof(address)); buf_add(buf, &netmask, sizeof(netmask)); buf_add(buf, &nexthop, sizeof(nexthop)); buf_add(buf, &metric, sizeof(metric)); free: TAILQ_REMOVE(r_list, entry, entry); delete_entry(entry->rr); free(entry); nentries++; } if (iface->auth_type == AUTH_CRYPT) auth_add_trailer(buf, iface); send_packet(iface, buf->buf, buf->wpos, &dst); buf_free(buf); } return (0); } void recv_request(struct iface *i, struct nbr *nbr, u_int8_t *buf, u_int16_t len) { struct rip_entry *e; struct rip_route rr; int l = len; bzero(&rr, sizeof(rr)); if (len < RIP_ENTRY_LEN) { log_debug("recv_request: bad packet size, interface %s", i->name); return; } /* * XXX is it guaranteed that bus is properly aligned. * If not this will bomb on strict alignment archs. * */ e = (struct rip_entry *)buf; if (len > RIP_ENTRY_LEN * MAX_RIP_ENTRIES) { log_debug("recv_request: packet too long\n"); return; } l -= RIP_ENTRY_LEN; /* * If there is exactly one entry in the request, and it has * an address family identifier of zero and a metric of * infinity (i.e., 16), then this is a request to send the * entire routing table. */ if (e->AFI == 0 && e->metric == ntohl(INFINITY) && l == 0) { ripe_imsg_compose_rde(IMSG_FULL_RESPONSE, nbr->peerid, 0, NULL, 0); return; } for ( ; l >= 0; l -= RIP_ENTRY_LEN) { if (e->AFI != AF_INET) { log_debug("recv_request: AFI %d not supported\n", e->AFI); return; } rr.address.s_addr = e->address; rr.mask.s_addr = e->mask; rr.nexthop.s_addr = e->nexthop; rr.metric = e->metric; rr.ifindex = i->ifindex; ripe_imsg_compose_rde(IMSG_ROUTE_REQUEST, nbr->peerid, 0, &rr, sizeof(rr)); e++; } ripe_imsg_compose_rde(IMSG_ROUTE_REQUEST_END, nbr->peerid, 0, NULL, 0); } void recv_response(struct iface *i, struct nbr *nbr, u_int8_t *buf, u_int16_t len) { struct rip_route r; struct rip_entry *e; int l; if (len < RIP_ENTRY_LEN) { log_debug("recv_response: bad packet size, interface %s", i->name); return; } /* We must double check the length, because the only entry * can be stripped off by authentication code */ if (len < RIP_ENTRY_LEN) { /* If there are no entries, our work is finished here */ return; } /* XXX again */ e = (struct rip_entry *)buf; if (len > RIP_ENTRY_LEN * MAX_RIP_ENTRIES) { log_debug("recv_response: packet too long\n"); return; } l = len - sizeof(*e); for ( ; l >= 0; l -= RIP_ENTRY_LEN) { if (ntohs(e->AFI) != AF_INET) { log_debug("recv_response: AFI %d not supported\n", e->AFI); return; } r.address.s_addr = e->address; r.mask.s_addr = e->mask; if (e->nexthop == INADDR_ANY || ((i->addr.s_addr & i->mask.s_addr) != (e->nexthop & i->mask.s_addr))) r.nexthop.s_addr = nbr->addr.s_addr; else r.nexthop.s_addr = e->nexthop; r.metric = ntohl(e->metric); r.ifindex = i->ifindex; ripe_imsg_compose_rde(IMSG_ROUTE_FEED, 0, 0, &r, sizeof(r)); e++; } }