📄 ospf_packet.c
字号:
/* * OSPF Sending and Receiving OSPF Packets. * Copyright (C) 1999, 2000 Toshiaki Takada * * This file is part of GNU Zebra. * * GNU Zebra is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any * later version. * * GNU Zebra is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */#include <zebra.h>#include "thread.h"#include "memory.h"#include "linklist.h"#include "prefix.h"#include "if.h"#include "table.h"#include "sockunion.h"#include "stream.h"#include "log.h"#include "md5-gnu.h"#include "ospfd/ospfd.h"#include "ospfd/ospf_network.h"#include "ospfd/ospf_interface.h"#include "ospfd/ospf_ism.h"#include "ospfd/ospf_asbr.h"#include "ospfd/ospf_lsa.h"#include "ospfd/ospf_lsdb.h"#include "ospfd/ospf_neighbor.h"#include "ospfd/ospf_nsm.h"#include "ospfd/ospf_packet.h"#include "ospfd/ospf_spf.h"#include "ospfd/ospf_flood.h"#include "ospfd/ospf_dump.h"static void ospf_ls_ack_send_list (struct ospf_interface *, list, struct in_addr);/* Packet Type String. */char *ospf_packet_type_str[] ={ "unknown", "Hello", "Database Description", "Link State Request", "Link State Update", "Link State Acknowledgment",};extern int in_cksum (void *ptr, int nbytes);/* OSPF authentication checking function */intospf_auth_type (struct ospf_interface *oi){ int auth_type; if (OSPF_IF_PARAM (oi, auth_type) == OSPF_AUTH_NOTSET) auth_type = oi->area->auth_type; else auth_type = OSPF_IF_PARAM (oi, auth_type); /* Handle case where MD5 key list is not configured aka Cisco */ if (auth_type == OSPF_AUTH_CRYPTOGRAPHIC && list_isempty (OSPF_IF_PARAM (oi, auth_crypt))) return OSPF_AUTH_NULL; return auth_type;}/* forward output pointer. */voidospf_output_forward (struct stream *s, int size){ s->putp += size;}struct ospf_packet *ospf_packet_new (size_t size){ struct ospf_packet *new; new = XCALLOC (MTYPE_OSPF_PACKET, sizeof (struct ospf_packet)); new->s = stream_new (size); return new;}voidospf_packet_free (struct ospf_packet *op){ if (op->s) stream_free (op->s); XFREE (MTYPE_OSPF_PACKET, op); op = NULL;}struct ospf_fifo *ospf_fifo_new (){ struct ospf_fifo *new; new = XCALLOC (MTYPE_OSPF_FIFO, sizeof (struct ospf_fifo)); return new;}/* Add new packet to fifo. */voidospf_fifo_push (struct ospf_fifo *fifo, struct ospf_packet *op){ if (fifo->tail) fifo->tail->next = op; else fifo->head = op; fifo->tail = op; fifo->count++;}/* Delete first packet from fifo. */struct ospf_packet *ospf_fifo_pop (struct ospf_fifo *fifo){ struct ospf_packet *op; op = fifo->head; if (op) { fifo->head = op->next; if (fifo->head == NULL) fifo->tail = NULL; fifo->count--; } return op;}/* Return first fifo entry. */struct ospf_packet *ospf_fifo_head (struct ospf_fifo *fifo){ return fifo->head;}/* Flush ospf packet fifo. */voidospf_fifo_flush (struct ospf_fifo *fifo){ struct ospf_packet *op; struct ospf_packet *next; for (op = fifo->head; op; op = next) { next = op->next; ospf_packet_free (op); } fifo->head = fifo->tail = NULL; fifo->count = 0;}/* Free ospf packet fifo. */voidospf_fifo_free (struct ospf_fifo *fifo){ ospf_fifo_flush (fifo); XFREE (MTYPE_OSPF_FIFO, fifo);}voidospf_packet_add (struct ospf_interface *oi, struct ospf_packet *op){ /* Add packet to end of queue. */ ospf_fifo_push (oi->obuf, op); /* Debug of packet fifo*/ /* ospf_fifo_debug (oi->obuf); */}voidospf_packet_delete (struct ospf_interface *oi){ struct ospf_packet *op; op = ospf_fifo_pop (oi->obuf); if (op) ospf_packet_free (op);}struct stream *ospf_stream_copy (struct stream *new, struct stream *s){ new->endp = s->endp; new->putp = s->putp; new->getp = s->getp; memcpy (new->data, s->data, stream_get_endp (s)); return new;}struct ospf_packet *ospf_packet_dup (struct ospf_packet *op){ struct ospf_packet *new; if (stream_get_endp(op->s) != op->length) zlog_warn ("ospf_packet_dup stream %ld ospf_packet %d size mismatch", STREAM_SIZE(op->s), op->length); /* Reserve space for MD5 authentication that may be added later. */ new = ospf_packet_new (stream_get_endp(op->s) + OSPF_AUTH_MD5_SIZE); ospf_stream_copy (new->s, op->s); new->dst = op->dst; new->length = op->length; return new;}intospf_packet_max (struct ospf_interface *oi){ int max; if ( ospf_auth_type (oi) == OSPF_AUTH_CRYPTOGRAPHIC) max = oi->ifp->mtu - OSPF_AUTH_MD5_SIZE - 88; else max = oi->ifp->mtu - 88; return max;}intospf_check_md5_digest (struct ospf_interface *oi, struct stream *s, u_int16_t length){ void *ibuf; struct md5_ctx ctx; unsigned char digest[OSPF_AUTH_MD5_SIZE]; unsigned char *pdigest; struct crypt_key *ck; struct ospf_header *ospfh; struct ospf_neighbor *nbr; ibuf = STREAM_PNT (s); ospfh = (struct ospf_header *) ibuf; /* Get pointer to the end of the packet. */ pdigest = ibuf + length; /* Get secret key. */ ck = ospf_crypt_key_lookup (OSPF_IF_PARAM (oi, auth_crypt), ospfh->u.crypt.key_id); if (ck == NULL) { zlog_warn ("interface %s: ospf_check_md5 no key %d", IF_NAME (oi), ospfh->u.crypt.key_id); return 0; } /* check crypto seqnum. */ nbr = ospf_nbr_lookup_by_routerid (oi->nbrs, &ospfh->router_id); if (nbr && ntohl(nbr->crypt_seqnum) > ntohl(ospfh->u.crypt.crypt_seqnum)) { zlog_warn ("interface %s: ospf_check_md5 bad sequence %d (expect %d)", IF_NAME (oi), ntohl(ospfh->u.crypt.crypt_seqnum), ntohl(nbr->crypt_seqnum)); return 0; } /* Generate a digest for the ospf packet - their digest + our digest. */ md5_init_ctx (&ctx); md5_process_bytes (ibuf, length, &ctx); md5_process_bytes (ck->auth_key, OSPF_AUTH_MD5_SIZE, &ctx); md5_finish_ctx (&ctx, digest); /* compare the two */ if (memcmp (pdigest, digest, OSPF_AUTH_MD5_SIZE)) { zlog_warn ("interface %s: ospf_check_md5 checksum mismatch", IF_NAME (oi)); return 0; } /* save neighbor's crypt_seqnum */ if (nbr) nbr->crypt_seqnum = ospfh->u.crypt.crypt_seqnum; return 1;}/* This function is called from ospf_write(), it will detect the authentication scheme and if it is MD5, it will change the sequence and update the MD5 digest. */intospf_make_md5_digest (struct ospf_interface *oi, struct ospf_packet *op){ struct ospf_header *ospfh; unsigned char digest[OSPF_AUTH_MD5_SIZE]; struct md5_ctx ctx; void *ibuf; unsigned long oldputp; u_int32_t t; struct crypt_key *ck; char *auth_key; ibuf = STREAM_DATA (op->s); ospfh = (struct ospf_header *) ibuf; if (ntohs (ospfh->auth_type) != OSPF_AUTH_CRYPTOGRAPHIC) return 0; /* We do this here so when we dup a packet, we don't have to waste CPU rewriting other headers. */ t = (time(NULL) & 0xFFFFFFFF); oi->crypt_seqnum = ( t > oi->crypt_seqnum ? t : oi->crypt_seqnum++); ospfh->u.crypt.crypt_seqnum = htonl (oi->crypt_seqnum); /* Get MD5 Authentication key from auth_key list. */ if (list_isempty (OSPF_IF_PARAM (oi, auth_crypt))) auth_key = ""; else { ck = getdata (OSPF_IF_PARAM (oi, auth_crypt)->tail); auth_key = ck->auth_key; } /* Generate a digest for the entire packet + our secret key. */ md5_init_ctx (&ctx); md5_process_bytes (ibuf, ntohs (ospfh->length), &ctx); md5_process_bytes (auth_key, OSPF_AUTH_MD5_SIZE, &ctx); md5_finish_ctx (&ctx, digest); /* Append md5 digest to the end of the stream. */ oldputp = stream_get_putp (op->s); stream_set_putp (op->s, ntohs (ospfh->length)); stream_put (op->s, digest, OSPF_AUTH_MD5_SIZE); stream_set_putp (op->s, oldputp); /* We do *NOT* increment the OSPF header length. */ op->length = ntohs (ospfh->length) + OSPF_AUTH_MD5_SIZE; if (stream_get_endp(op->s) != op->length) zlog_warn("ospf_make_md5_digest: length mismatch stream %ld ospf_packet %d", stream_get_endp(op->s), op->length); return OSPF_AUTH_MD5_SIZE;}intospf_ls_req_timer (struct thread *thread){ struct ospf_neighbor *nbr; nbr = THREAD_ARG (thread); nbr->t_ls_req = NULL; /* Send Link State Request. */ if (ospf_ls_request_count (nbr)) ospf_ls_req_send (nbr); /* Set Link State Request retransmission timer. */ OSPF_NSM_TIMER_ON (nbr->t_ls_req, ospf_ls_req_timer, nbr->v_ls_req); return 0;}voidospf_ls_req_event (struct ospf_neighbor *nbr){ if (nbr->t_ls_req) { thread_cancel (nbr->t_ls_req); nbr->t_ls_req = NULL; } nbr->t_ls_req = thread_add_event (master, ospf_ls_req_timer, nbr, 0);}/* Cyclic timer function. Fist registered in ospf_nbr_new () in ospf_neighbor.c */intospf_ls_upd_timer (struct thread *thread){ struct ospf_neighbor *nbr; nbr = THREAD_ARG (thread); nbr->t_ls_upd = NULL; /* Send Link State Update. */ if (ospf_ls_retransmit_count (nbr) > 0) { list update; struct ospf_lsdb *lsdb; int i; struct timeval now; int retransmit_interval; gettimeofday (&now, NULL); retransmit_interval = OSPF_IF_PARAM (nbr->oi, retransmit_interval); lsdb = &nbr->ls_rxmt; update = list_new (); for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) { struct route_table *table = lsdb->type[i].db; struct route_node *rn; for (rn = route_top (table); rn; rn = route_next (rn)) { struct ospf_lsa *lsa; if ((lsa = rn->info) != NULL) /* Don't retransmit an LSA if we received it within the last RxmtInterval seconds - this is to allow the neighbour a chance to acknowledge the LSA as it may have ben just received before the retransmit timer fired. This is a small tweak to what is in the RFC, but it will cut out out a lot of retransmit traffic - MAG */ if (tv_cmp (tv_sub (now, lsa->tv_recv), int2tv (retransmit_interval)) >= 0) listnode_add (update, rn->info); } } if (listcount (update) > 0) ospf_ls_upd_send (nbr, update, OSPF_SEND_PACKET_DIRECT); list_delete (update); } /* Set LS Update retransmission timer. */ OSPF_NSM_TIMER_ON (nbr->t_ls_upd, ospf_ls_upd_timer, nbr->v_ls_upd); return 0;}intospf_ls_ack_timer (struct thread *thread){ struct ospf_interface *oi; oi = THREAD_ARG (thread); oi->t_ls_ack = NULL; /* Send Link State Acknowledgment. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -