diff options
author | Claudio Jeker <claudio@cvs.openbsd.org> | 2005-01-28 14:05:41 +0000 |
---|---|---|
committer | Claudio Jeker <claudio@cvs.openbsd.org> | 2005-01-28 14:05:41 +0000 |
commit | ccc5f735cc0a287cd117c41e690e7bab855d35df (patch) | |
tree | 73ce6ba377048b4358b66dcd68c700d22eea33eb /usr.sbin/ospfd/packet.c | |
parent | 0dcb4b3a6285e3cc2e96ba56db5f830a086696f7 (diff) |
Welcome ospfd
started by Esben Norby some time ago by using the imsg/three process framework
of bgpd. He implemented the basic concept plus the ospf finite state machines.
Later I joined and helped him cleanup, debug and extend his work.
Right now it is not particularly useful, major parts are still missing but is
imported to allow more people to work on it.
status:
The basic protocol works for broadcast networks and the LS database is
synchronized and updated. It is not possible to be DR or BDR on a network
and other interface types like point-to-point are not yet supported.
The shortest path tree is not calculated and so no routing information is
exchanged with the kernel FIB.
Not yet connected to the builds.
OK henning@
Diffstat (limited to 'usr.sbin/ospfd/packet.c')
-rw-r--r-- | usr.sbin/ospfd/packet.c | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/usr.sbin/ospfd/packet.c b/usr.sbin/ospfd/packet.c new file mode 100644 index 00000000000..b76d9c22db4 --- /dev/null +++ b/usr.sbin/ospfd/packet.c @@ -0,0 +1,288 @@ +/* $OpenBSD: packet.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */ + +/* + * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com> + * + * 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/time.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <event.h> +#include <stdlib.h> +#include <strings.h> + +#include "ospfd.h" +#include "ospf.h" +#include "log.h" +#include "ospfe.h" + +int ip_hdr_sanity_check(const struct ip *, u_int16_t); +int ospf_hdr_sanity_check(const struct ip *, + struct ospf_hdr *, u_int16_t, const struct iface *); +struct iface *find_iface(struct ospfd_conf *, struct in_addr); + +void +gen_ospf_hdr(void *buf, struct iface *iface, u_int8_t type) +{ + struct ospf_hdr *ospf_hdr = buf; + + ospf_hdr->version = OSPF_VERSION; + ospf_hdr->type = type; + ospf_hdr->len = 0; /* updated later */ + ospf_hdr->rtr_id = iface->rtr_id.s_addr; + ospf_hdr->area_id = iface->area->id.s_addr; + ospf_hdr->chksum = 0; /* updated later */ + ospf_hdr->auth_type = htons(iface->auth_type); +} + +/* send and receive packets */ +int +send_packet(struct iface *iface, char *pkt, int len, struct sockaddr_in *dst) +{ + if (iface->passive) { + log_warnx("send_packet: cannot send packet on passive " + "interface %s", iface->name); + return (-1); + } + + /* set outgoing interface for multicast traffic */ + if (IN_MULTICAST(ntohl(dst->sin_addr.s_addr))) + if (if_set_mcast(iface) == -1) { + log_debug("send_packet: error setting multicast " + "interface, %s", iface->name); + return (-1); + } + + if (sendto(iface->fd, pkt, len, 0, + (struct sockaddr *)dst, sizeof(*dst)) == -1 ) { + log_warnx("send_packet: error sending packet on interface %s", + iface->name); + return (-1); + } + + return (0); +} + +void +recv_packet(int fd, short event, void *bula) +{ + struct ospfd_conf *xconf = bula; + struct ip ip_hdr; + struct ospf_hdr *ospf_hdr; + struct iface *iface; + struct nbr *nbr = NULL; + struct in_addr addr; + char *buf, *ptr; + ssize_t r; + u_int16_t len; + int l; + + if (event != EV_READ) + return; + + /* + * XXX I don't like to allocate a buffer for each packet received + * and freeing that buffer at the end of the function. It would be + * enough to allocate the buffer on startup. + */ + if ((ptr = buf = calloc(1, READ_BUF_SIZE)) == NULL) + fatal("recv_packet"); + + if ((r = recvfrom(fd, buf, READ_BUF_SIZE, 0, NULL, NULL)) == -1) { + if (errno != EAGAIN) { + log_debug("recv_packet: error receiving packet"); + } + goto done; + } + + len = (u_int16_t)r; + + /* IP header sanity checks */ + if (len < sizeof(ip_hdr)) { + log_warnx("recv_packet: bad packet size"); + goto done; + } + memcpy(&ip_hdr, buf, sizeof(ip_hdr)); + if ((l = ip_hdr_sanity_check(&ip_hdr, len)) == -1) + goto done; + buf += l; + len -= l; + + /* find a matching interface */ + if ((iface = find_iface(xconf, ip_hdr.ip_src)) == NULL) { + log_debug("recv_packet: cannot find valid interface"); + goto done; + } + + /* + * Packet needs to be sent to AllSPFRouters or AllDRouters + * or to the address of the interface itself. + * AllDRouters is only valid for DR and BDR but this is checked later. + */ + inet_aton(AllSPFRouters, &addr); + if (ip_hdr.ip_dst.s_addr != addr.s_addr) { + inet_aton(AllDRouters, &addr); + if (ip_hdr.ip_dst.s_addr != addr.s_addr) { + if (ip_hdr.ip_dst.s_addr != iface->addr.s_addr) { + log_debug("recv_packet: packet sent to wrong " + "address %s, interface %s", + inet_ntoa(ip_hdr.ip_dst), iface->name); + goto done; + } + } + } + + /* OSPF header sanity checks */ + if (len < sizeof(*ospf_hdr)) { + log_warnx("recv_packet: bad packet size"); + goto done; + } + ospf_hdr = (struct ospf_hdr *)buf; + + if ((l = ospf_hdr_sanity_check(&ip_hdr, ospf_hdr, len, iface)) == -1) + goto done; + + buf += sizeof(*ospf_hdr); + len = l - sizeof(*ospf_hdr); + + if (ospf_hdr->type != PACKET_TYPE_HELLO) + /* find neighbor */ + if ((nbr = nbr_find_id(iface, ospf_hdr->rtr_id)) == NULL) { + log_debug("recv_packet: unknown neighbor ID"); + goto done; + } + + /* switch OSPF packet type */ + switch (ospf_hdr->type) { + case PACKET_TYPE_HELLO: + inet_aton(AllDRouters, &addr); + if (ip_hdr.ip_dst.s_addr == addr.s_addr) { + log_debug("recv_packet: invalid destination IP " + "address"); + break; + } + + recv_hello(iface, ip_hdr.ip_src, ospf_hdr->rtr_id, buf, len); + break; + case PACKET_TYPE_DD: + recv_db_description(nbr, buf, len); + break; + case PACKET_TYPE_LS_REQUEST: + recv_ls_req(nbr, buf, len); + break; + case PACKET_TYPE_LS_UPDATE: + recv_ls_update(nbr, buf, len); + break; + case PACKET_TYPE_LS_ACK: + recv_ls_ack(nbr, buf, len); + break; + default: + log_debug("recv_packet: unknown OSPF packet type, interface %s", + iface->name); + } +done: + free(ptr); + return; +} + +int +ip_hdr_sanity_check(const struct ip *ip_hdr, u_int16_t len) +{ + if (ntohs(ip_hdr->ip_len) != len) { + log_debug("recv_packet: invalid IP packet length %s", + ntohs(ip_hdr->ip_len)); + return (-1); + } + + if (ip_hdr->ip_p != IPPROTO_OSPF) + /* this is enforced by the socket itself */ + fatalx("recv_packet: invalid IP proto"); + + return (ip_hdr->ip_hl << 2); +} + +int +ospf_hdr_sanity_check(const struct ip *ip_hdr, struct ospf_hdr *ospf_hdr, + u_int16_t len, const struct iface *iface) +{ + struct in_addr addr; + + if (ospf_hdr->version != OSPF_VERSION) { + log_debug("recv_packet: invalid OSPF version %d", + ospf_hdr->version); + return (-1); + } + + if (ntohs(ospf_hdr->len) > len || + len <= sizeof(struct ospf_hdr)) { + log_debug("recv_packet: invalid OSPF packet length %d", + ntohs(ospf_hdr->len)); + return (-1); + } + + if (ospf_hdr->area_id != iface->area->id.s_addr) { + /* TODO backbone area is allowed for virtual links */ + addr.s_addr = ospf_hdr->area_id; + log_debug("recv_packet: invalid area ID %s, interface %s", + inet_ntoa(addr), iface->name); + return (-1); + } + + if (iface->type == IF_TYPE_BROADCAST || iface->type == IF_TYPE_NBMA) { + if (inet_aton(AllDRouters, &addr) == 0) + fatalx("recv_packet: inet_aton"); + if ((ip_hdr->ip_dst.s_addr == addr.s_addr) && + (iface->state != (IF_STA_DR | IF_STA_BACKUP))) { + log_debug("recv_packet: invalid destination IP in " + "state %s, interface %s", + if_state_name(iface->state), iface->name); + return (-1); + } + } + + if (auth_validate(ospf_hdr, iface)) { + log_warnx("recv_packet: authentication error, interface %s", + iface->name); + return (-1); + } + + return (ntohs(ospf_hdr->len)); +} + +struct iface * +find_iface(struct ospfd_conf *xconf, struct in_addr src) +{ + struct area *area = NULL; + struct iface *iface = NULL; + + /* returned interface needs to be active */ + LIST_FOREACH(area, &xconf->area_list, entry) { + LIST_FOREACH(iface, &area->iface_list, entry) { + if (iface->fd > 0 && (iface->addr.s_addr & + iface->mask.s_addr) == (src.s_addr & + iface->mask.s_addr) && !iface->passive) + return (iface); + } + } + + return (NULL); +} |