Coverage Report

Created: 2025-07-14 06:48

/src/frr/ospfd/ospf_api.c
Line
Count
Source (jump to first uncovered line)
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
 * API message handling module for OSPF daemon and client.
4
 * Copyright (C) 2001, 2002 Ralph Keller
5
 * Copyright (c) 2022, LabN Consulting, L.L.C.
6
 */
7
8
#include <zebra.h>
9
10
#ifdef SUPPORT_OSPF_API
11
12
#include "linklist.h"
13
#include "prefix.h"
14
#include "if.h"
15
#include "table.h"
16
#include "memory.h"
17
#include "command.h"
18
#include "vty.h"
19
#include "stream.h"
20
#include "log.h"
21
#include "frrevent.h"
22
#include "hash.h"
23
#include "sockunion.h" /* for inet_aton() */
24
#include "buffer.h"
25
#include "network.h"
26
27
#include "ospfd/ospfd.h"
28
#include "ospfd/ospf_interface.h"
29
#include "ospfd/ospf_ism.h"
30
#include "ospfd/ospf_asbr.h"
31
#include "ospfd/ospf_lsa.h"
32
#include "ospfd/ospf_lsdb.h"
33
#include "ospfd/ospf_neighbor.h"
34
#include "ospfd/ospf_nsm.h"
35
#include "ospfd/ospf_flood.h"
36
#include "ospfd/ospf_packet.h"
37
#include "ospfd/ospf_spf.h"
38
#include "ospfd/ospf_dump.h"
39
#include "ospfd/ospf_route.h"
40
#include "ospfd/ospf_ase.h"
41
#include "ospfd/ospf_zebra.h"
42
43
#include "ospfd/ospf_api.h"
44
45
46
/* For debugging only, will be removed */
47
void api_opaque_lsa_print(struct ospf_lsa *lsa)
48
0
{
49
0
  struct opaque_lsa {
50
0
    struct lsa_header header;
51
0
    uint8_t mydata[];
52
0
  };
53
54
0
  struct opaque_lsa *olsa;
55
0
  int opaquelen;
56
0
  int i;
57
58
0
  ospf_lsa_header_dump(lsa->data);
59
60
0
  olsa = (struct opaque_lsa *)lsa->data;
61
62
0
  opaquelen = lsa->size - OSPF_LSA_HEADER_SIZE;
63
0
  zlog_debug("apiserver_lsa_print: opaquelen=%d", opaquelen);
64
65
0
  for (i = 0; i < opaquelen; i++) {
66
0
    zlog_debug("0x%x ", olsa->mydata[i]);
67
0
  }
68
0
  zlog_debug(" ");
69
0
}
70
71
/* -----------------------------------------------------------
72
 * Generic messages
73
 * -----------------------------------------------------------
74
 */
75
76
struct msg *msg_new(uint8_t msgtype, void *msgbody, uint32_t seqnum,
77
        uint16_t msglen)
78
0
{
79
0
  struct msg *new;
80
81
0
  new = XCALLOC(MTYPE_OSPF_API_MSG, sizeof(struct msg));
82
83
0
  new->hdr.version = OSPF_API_VERSION;
84
0
  new->hdr.msgtype = msgtype;
85
0
  new->hdr.msglen = htons(msglen);
86
0
  new->hdr.msgseq = htonl(seqnum);
87
88
0
  new->s = stream_new(msglen);
89
0
  assert(new->s);
90
0
  stream_put(new->s, msgbody, msglen);
91
92
0
  return new;
93
0
}
94
95
96
/* Duplicate a message by copying content. */
97
struct msg *msg_dup(struct msg *msg)
98
0
{
99
0
  struct msg *new;
100
0
  size_t size;
101
102
0
  assert(msg);
103
104
0
  size = ntohs(msg->hdr.msglen);
105
0
  if (size > OSPF_MAX_LSA_SIZE)
106
0
    return NULL;
107
108
0
  new = msg_new(msg->hdr.msgtype, STREAM_DATA(msg->s),
109
0
          ntohl(msg->hdr.msgseq), size);
110
0
  return new;
111
0
}
112
113
114
/* XXX only for testing, will be removed */
115
116
struct nametab {
117
  int value;
118
  const char *name;
119
};
120
121
const char *ospf_api_typename(int msgtype)
122
0
{
123
0
  struct nametab NameTab[] = {
124
0
    {
125
0
      MSG_REGISTER_OPAQUETYPE, "Register opaque-type",
126
0
    },
127
0
    {
128
0
      MSG_UNREGISTER_OPAQUETYPE, "Unregister opaque-type",
129
0
    },
130
0
    {
131
0
      MSG_REGISTER_EVENT, "Register event",
132
0
    },
133
0
    {
134
0
      MSG_SYNC_LSDB, "Sync LSDB",
135
0
    },
136
0
    {
137
0
      MSG_ORIGINATE_REQUEST, "Originate request",
138
0
    },
139
0
    {
140
0
      MSG_DELETE_REQUEST, "Delete request",
141
0
    },
142
0
    {
143
0
      MSG_REPLY, "Reply",
144
0
    },
145
0
    {
146
0
      MSG_READY_NOTIFY, "Ready notify",
147
0
    },
148
0
    {
149
0
      MSG_LSA_UPDATE_NOTIFY, "LSA update notify",
150
0
    },
151
0
    {
152
0
      MSG_LSA_DELETE_NOTIFY, "LSA delete notify",
153
0
    },
154
0
    {
155
0
      MSG_NEW_IF, "New interface",
156
0
    },
157
0
    {
158
0
      MSG_DEL_IF, "Del interface",
159
0
    },
160
0
    {
161
0
      MSG_ISM_CHANGE, "ISM change",
162
0
    },
163
0
    {
164
0
      MSG_NSM_CHANGE, "NSM change",
165
0
    },
166
0
    {
167
0
      MSG_REACHABLE_CHANGE,
168
0
      "Reachable change",
169
0
    },
170
0
  };
171
172
0
  int i, n = array_size(NameTab);
173
0
  const char *name = NULL;
174
175
0
  for (i = 0; i < n; i++) {
176
0
    if (NameTab[i].value == msgtype) {
177
0
      name = NameTab[i].name;
178
0
      break;
179
0
    }
180
0
  }
181
182
0
  return name ? name : "?";
183
0
}
184
185
const char *ospf_api_errname(int errcode)
186
0
{
187
0
  struct nametab NameTab[] = {
188
0
    {
189
0
      OSPF_API_OK, "OK",
190
0
    },
191
0
    {
192
0
      OSPF_API_NOSUCHINTERFACE, "No such interface",
193
0
    },
194
0
    {
195
0
      OSPF_API_NOSUCHAREA, "No such area",
196
0
    },
197
0
    {
198
0
      OSPF_API_NOSUCHLSA, "No such LSA",
199
0
    },
200
0
    {
201
0
      OSPF_API_ILLEGALLSATYPE, "Illegal LSA type",
202
0
    },
203
0
    {
204
0
      OSPF_API_OPAQUETYPEINUSE, "Opaque type in use",
205
0
    },
206
0
    {
207
0
      OSPF_API_OPAQUETYPENOTREGISTERED,
208
0
      "Opaque type not registered",
209
0
    },
210
0
    {
211
0
      OSPF_API_NOTREADY, "Not ready",
212
0
    },
213
0
    {
214
0
      OSPF_API_NOMEMORY, "No memory",
215
0
    },
216
0
    {
217
0
      OSPF_API_ERROR, "Other error",
218
0
    },
219
0
    {
220
0
      OSPF_API_UNDEF, "Undefined",
221
0
    },
222
0
  };
223
224
0
  int i, n = array_size(NameTab);
225
0
  const char *name = NULL;
226
227
0
  for (i = 0; i < n; i++) {
228
0
    if (NameTab[i].value == errcode) {
229
0
      name = NameTab[i].name;
230
0
      break;
231
0
    }
232
0
  }
233
234
0
  return name ? name : "?";
235
0
}
236
237
void msg_print(struct msg *msg)
238
0
{
239
0
  if (!msg) {
240
0
    zlog_debug("msg_print msg=NULL!");
241
0
    return;
242
0
  }
243
244
  /* API message common header part. */
245
0
  zlog_debug("API-msg [%s]: type(%d),len(%d),seq(%lu),data(%p),size(%zd)",
246
0
       ospf_api_typename(msg->hdr.msgtype), msg->hdr.msgtype,
247
0
       ntohs(msg->hdr.msglen),
248
0
       (unsigned long)ntohl(msg->hdr.msgseq), STREAM_DATA(msg->s),
249
0
       STREAM_SIZE(msg->s));
250
251
0
  return;
252
0
}
253
254
void msg_free(struct msg *msg)
255
0
{
256
0
  if (msg->s)
257
0
    stream_free(msg->s);
258
259
0
  XFREE(MTYPE_OSPF_API_MSG, msg);
260
0
}
261
262
263
/* Set sequence number of message */
264
void msg_set_seq(struct msg *msg, uint32_t seqnr)
265
0
{
266
0
  assert(msg);
267
0
  msg->hdr.msgseq = htonl(seqnr);
268
0
}
269
270
/* Get sequence number of message */
271
uint32_t msg_get_seq(struct msg *msg)
272
0
{
273
0
  assert(msg);
274
0
  return ntohl(msg->hdr.msgseq);
275
0
}
276
277
/* -----------------------------------------------------------
278
 * Message fifo queues
279
 * -----------------------------------------------------------
280
 */
281
282
struct msg_fifo *msg_fifo_new(void)
283
0
{
284
0
  return XCALLOC(MTYPE_OSPF_API_FIFO, sizeof(struct msg_fifo));
285
0
}
286
287
/* Add new message to fifo. */
288
void msg_fifo_push(struct msg_fifo *fifo, struct msg *msg)
289
0
{
290
0
  if (fifo->tail)
291
0
    fifo->tail->next = msg;
292
0
  else
293
0
    fifo->head = msg;
294
295
0
  fifo->tail = msg;
296
0
  fifo->count++;
297
0
}
298
299
300
/* Remove first message from fifo. */
301
struct msg *msg_fifo_pop(struct msg_fifo *fifo)
302
0
{
303
0
  struct msg *msg;
304
305
0
  msg = fifo->head;
306
0
  if (msg) {
307
0
    fifo->head = msg->next;
308
309
0
    if (fifo->head == NULL)
310
0
      fifo->tail = NULL;
311
312
0
    fifo->count--;
313
0
  }
314
0
  return msg;
315
0
}
316
317
/* Return first fifo entry but do not remove it. */
318
struct msg *msg_fifo_head(struct msg_fifo *fifo)
319
0
{
320
0
  return fifo->head;
321
0
}
322
323
/* Flush message fifo. */
324
void msg_fifo_flush(struct msg_fifo *fifo)
325
0
{
326
0
  struct msg *op;
327
0
  struct msg *next;
328
329
0
  for (op = fifo->head; op; op = next) {
330
0
    next = op->next;
331
0
    msg_free(op);
332
0
  }
333
334
0
  fifo->head = fifo->tail = NULL;
335
0
  fifo->count = 0;
336
0
}
337
338
/* Free API message fifo. */
339
void msg_fifo_free(struct msg_fifo *fifo)
340
0
{
341
0
  msg_fifo_flush(fifo);
342
343
0
  XFREE(MTYPE_OSPF_API_FIFO, fifo);
344
0
}
345
346
struct msg *msg_read(int fd)
347
0
{
348
0
  struct msg *msg;
349
0
  struct apimsghdr hdr;
350
0
  uint8_t buf[OSPF_API_MAX_MSG_SIZE];
351
0
  ssize_t bodylen;
352
0
  ssize_t rlen;
353
354
  /* Read message header */
355
0
  rlen = readn(fd, (uint8_t *)&hdr, sizeof(struct apimsghdr));
356
357
0
  if (rlen < 0) {
358
0
    zlog_warn("msg_read: readn %s", safe_strerror(errno));
359
0
    return NULL;
360
0
  } else if (rlen == 0) {
361
0
    zlog_warn("msg_read: Connection closed by peer");
362
0
    return NULL;
363
0
  } else if (rlen != sizeof(struct apimsghdr)) {
364
0
    zlog_warn("msg_read: Cannot read message header!");
365
0
    return NULL;
366
0
  }
367
368
  /* Check version of API protocol */
369
0
  if (hdr.version != OSPF_API_VERSION) {
370
0
    zlog_warn("msg_read: OSPF API protocol version mismatch");
371
0
    return NULL;
372
0
  }
373
374
  /* Determine body length. */
375
0
  bodylen = ntohs(hdr.msglen);
376
0
  if (bodylen > (ssize_t)sizeof(buf)) {
377
0
    zlog_warn("%s: Body Length of message greater than what we can read",
378
0
        __func__);
379
0
    return NULL;
380
0
  }
381
382
0
  if (bodylen > 0) {
383
    /* Read message body */
384
0
    rlen = readn(fd, buf, bodylen);
385
0
    if (rlen < 0) {
386
0
      zlog_warn("msg_read: readn %s", safe_strerror(errno));
387
0
      return NULL;
388
0
    } else if (rlen == 0) {
389
0
      zlog_warn("msg_read: Connection closed by peer");
390
0
      return NULL;
391
0
    } else if (rlen != bodylen) {
392
0
      zlog_warn("msg_read: Cannot read message body!");
393
0
      return NULL;
394
0
    }
395
0
  }
396
397
  /* Allocate new message */
398
0
  msg = msg_new(hdr.msgtype, buf, ntohl(hdr.msgseq), bodylen);
399
400
0
  return msg;
401
0
}
402
403
int msg_write(int fd, struct msg *msg)
404
0
{
405
0
  uint8_t buf[OSPF_API_MAX_MSG_SIZE];
406
0
  uint16_t l;
407
0
  int wlen;
408
409
0
  assert(msg);
410
0
  assert(msg->s);
411
412
  /* Length of OSPF LSA payload */
413
0
  l = ntohs(msg->hdr.msglen);
414
0
  if (l > OSPF_MAX_LSA_SIZE) {
415
0
    zlog_warn("%s: wrong LSA size %d", __func__, l);
416
0
    return -1;
417
0
  }
418
419
  /* Make contiguous memory buffer for message */
420
0
  memcpy(buf, &msg->hdr, sizeof(struct apimsghdr));
421
0
  memcpy(buf + sizeof(struct apimsghdr), STREAM_DATA(msg->s), l);
422
423
  /* Total length of OSPF API Message */
424
0
  l += sizeof(struct apimsghdr);
425
0
  wlen = writen(fd, buf, l);
426
0
  if (wlen < 0) {
427
0
    zlog_warn("%s: writen %s", __func__, safe_strerror(errno));
428
0
    return -1;
429
0
  } else if (wlen == 0) {
430
0
    zlog_warn("%s: Connection closed by peer", __func__);
431
0
    return -1;
432
0
  } else if (wlen != l) {
433
0
    zlog_warn("%s: Cannot write API message", __func__);
434
0
    return -1;
435
0
  }
436
0
  return 0;
437
0
}
438
439
/* -----------------------------------------------------------
440
 * Specific messages
441
 * -----------------------------------------------------------
442
 */
443
444
struct msg *new_msg_register_opaque_type(uint32_t seqnum, uint8_t ltype,
445
           uint8_t otype)
446
0
{
447
0
  struct msg_register_opaque_type rmsg;
448
449
0
  rmsg.lsatype = ltype;
450
0
  rmsg.opaquetype = otype;
451
0
  memset(&rmsg.pad, 0, sizeof(rmsg.pad));
452
453
0
  return msg_new(MSG_REGISTER_OPAQUETYPE, &rmsg, seqnum,
454
0
           sizeof(struct msg_register_opaque_type));
455
0
}
456
457
struct msg *new_msg_register_event(uint32_t seqnum,
458
           struct lsa_filter_type *filter)
459
0
{
460
0
  uint8_t buf[OSPF_API_MAX_MSG_SIZE];
461
0
  struct msg_register_event *emsg;
462
0
  unsigned int len;
463
464
0
  emsg = (struct msg_register_event *)buf;
465
0
  len = sizeof(struct msg_register_event)
466
0
        + filter->num_areas * sizeof(struct in_addr);
467
0
  emsg->filter.typemask = htons(filter->typemask);
468
0
  emsg->filter.origin = filter->origin;
469
0
  emsg->filter.num_areas = filter->num_areas;
470
0
  if (len > sizeof(buf))
471
0
    len = sizeof(buf);
472
  /* API broken - missing memcpy to fill data */
473
0
  return msg_new(MSG_REGISTER_EVENT, emsg, seqnum, len);
474
0
}
475
476
struct msg *new_msg_sync_lsdb(uint32_t seqnum, struct lsa_filter_type *filter)
477
0
{
478
0
  uint8_t buf[OSPF_API_MAX_MSG_SIZE];
479
0
  struct msg_sync_lsdb *smsg;
480
0
  unsigned int len;
481
482
0
  smsg = (struct msg_sync_lsdb *)buf;
483
0
  len = sizeof(struct msg_sync_lsdb)
484
0
        + filter->num_areas * sizeof(struct in_addr);
485
0
  smsg->filter.typemask = htons(filter->typemask);
486
0
  smsg->filter.origin = filter->origin;
487
0
  smsg->filter.num_areas = filter->num_areas;
488
0
  if (len > sizeof(buf))
489
0
    len = sizeof(buf);
490
  /* API broken - missing memcpy to fill data */
491
0
  return msg_new(MSG_SYNC_LSDB, smsg, seqnum, len);
492
0
}
493
494
495
struct msg *new_msg_originate_request(uint32_t seqnum, struct in_addr ifaddr,
496
              struct in_addr area_id,
497
              struct lsa_header *data)
498
0
{
499
0
  struct msg_originate_request *omsg;
500
0
  unsigned int omsglen;
501
0
  char buf[OSPF_API_MAX_MSG_SIZE];
502
0
  size_t off_data = offsetof(struct msg_originate_request, data);
503
0
  size_t data_maxs = sizeof(buf) - off_data;
504
0
  struct lsa_header *omsg_data = (struct lsa_header *)&buf[off_data];
505
506
0
  omsg = (struct msg_originate_request *)buf;
507
0
  omsg->ifaddr = ifaddr;
508
0
  omsg->area_id = area_id;
509
510
0
  omsglen = ntohs(data->length);
511
0
  if (omsglen > data_maxs)
512
0
    omsglen = data_maxs;
513
0
  memcpy(omsg_data, data, omsglen);
514
0
  omsglen += sizeof(struct msg_originate_request)
515
0
       - sizeof(struct lsa_header);
516
517
0
  return msg_new(MSG_ORIGINATE_REQUEST, omsg, seqnum, omsglen);
518
0
}
519
520
struct msg *new_msg_delete_request(uint32_t seqnum, struct in_addr addr,
521
           uint8_t lsa_type, uint8_t opaque_type,
522
           uint32_t opaque_id, uint8_t flags)
523
0
{
524
0
  struct msg_delete_request dmsg;
525
0
  dmsg.addr = addr;
526
0
  dmsg.lsa_type = lsa_type;
527
0
  dmsg.opaque_type = opaque_type;
528
0
  dmsg.opaque_id = htonl(opaque_id);
529
0
  memset(&dmsg.pad, 0, sizeof(dmsg.pad));
530
0
  dmsg.flags = flags;
531
532
0
  return msg_new(MSG_DELETE_REQUEST, &dmsg, seqnum,
533
0
           sizeof(struct msg_delete_request));
534
0
}
535
536
537
struct msg *new_msg_reply(uint32_t seqnr, uint8_t rc)
538
0
{
539
0
  struct msg *msg;
540
0
  struct msg_reply rmsg;
541
542
  /* Set return code */
543
0
  rmsg.errcode = rc;
544
0
  memset(&rmsg.pad, 0, sizeof(rmsg.pad));
545
546
0
  msg = msg_new(MSG_REPLY, &rmsg, seqnr, sizeof(struct msg_reply));
547
548
0
  return msg;
549
0
}
550
551
struct msg *new_msg_ready_notify(uint32_t seqnr, uint8_t lsa_type,
552
         uint8_t opaque_type, struct in_addr addr)
553
0
{
554
0
  struct msg_ready_notify rmsg;
555
556
0
  rmsg.lsa_type = lsa_type;
557
0
  rmsg.opaque_type = opaque_type;
558
0
  memset(&rmsg.pad, 0, sizeof(rmsg.pad));
559
0
  rmsg.addr = addr;
560
561
0
  return msg_new(MSG_READY_NOTIFY, &rmsg, seqnr,
562
0
           sizeof(struct msg_ready_notify));
563
0
}
564
565
struct msg *new_msg_new_if(uint32_t seqnr, struct in_addr ifaddr,
566
         struct in_addr area_id)
567
0
{
568
0
  struct msg_new_if nmsg;
569
570
0
  nmsg.ifaddr = ifaddr;
571
0
  nmsg.area_id = area_id;
572
573
0
  return msg_new(MSG_NEW_IF, &nmsg, seqnr, sizeof(struct msg_new_if));
574
0
}
575
576
struct msg *new_msg_del_if(uint32_t seqnr, struct in_addr ifaddr)
577
0
{
578
0
  struct msg_del_if dmsg;
579
580
0
  dmsg.ifaddr = ifaddr;
581
582
0
  return msg_new(MSG_DEL_IF, &dmsg, seqnr, sizeof(struct msg_del_if));
583
0
}
584
585
struct msg *new_msg_ism_change(uint32_t seqnr, struct in_addr ifaddr,
586
             struct in_addr area_id, uint8_t status)
587
0
{
588
0
  struct msg_ism_change imsg;
589
590
0
  imsg.ifaddr = ifaddr;
591
0
  imsg.area_id = area_id;
592
0
  imsg.status = status;
593
0
  memset(&imsg.pad, 0, sizeof(imsg.pad));
594
595
0
  return msg_new(MSG_ISM_CHANGE, &imsg, seqnr,
596
0
           sizeof(struct msg_ism_change));
597
0
}
598
599
struct msg *new_msg_nsm_change(uint32_t seqnr, struct in_addr ifaddr,
600
             struct in_addr nbraddr, struct in_addr router_id,
601
             uint8_t status)
602
0
{
603
0
  struct msg_nsm_change nmsg;
604
605
0
  nmsg.ifaddr = ifaddr;
606
0
  nmsg.nbraddr = nbraddr;
607
0
  nmsg.router_id = router_id;
608
0
  nmsg.status = status;
609
0
  memset(&nmsg.pad, 0, sizeof(nmsg.pad));
610
611
0
  return msg_new(MSG_NSM_CHANGE, &nmsg, seqnr,
612
0
           sizeof(struct msg_nsm_change));
613
0
}
614
615
struct msg *new_msg_lsa_change_notify(uint8_t msgtype, uint32_t seqnum,
616
              struct in_addr ifaddr,
617
              struct in_addr area_id,
618
              uint8_t is_self_originated,
619
              struct lsa_header *data)
620
0
{
621
0
  uint8_t buf[OSPF_API_MAX_MSG_SIZE];
622
0
  struct msg_lsa_change_notify *nmsg;
623
0
  unsigned int len;
624
0
  size_t off_data = offsetof(struct msg_lsa_change_notify, data);
625
0
  size_t data_maxs = sizeof(buf) - off_data;
626
0
  struct lsa_header *nmsg_data = (struct lsa_header *)&buf[off_data];
627
628
0
  assert(data);
629
630
0
  nmsg = (struct msg_lsa_change_notify *)buf;
631
0
  nmsg->ifaddr = ifaddr;
632
0
  nmsg->area_id = area_id;
633
0
  nmsg->is_self_originated = is_self_originated;
634
0
  memset(&nmsg->pad, 0, sizeof(nmsg->pad));
635
636
0
  len = ntohs(data->length);
637
0
  if (len > data_maxs)
638
0
    len = data_maxs;
639
0
  memcpy(nmsg_data, data, len);
640
0
  len += sizeof(struct msg_lsa_change_notify) - sizeof(struct lsa_header);
641
642
0
  return msg_new(msgtype, nmsg, seqnum, len);
643
0
}
644
645
struct msg *new_msg_reachable_change(uint32_t seqnum, uint16_t nadd,
646
             struct in_addr *add, uint16_t nremove,
647
             struct in_addr *remove)
648
0
{
649
0
  uint8_t buf[OSPF_API_MAX_MSG_SIZE];
650
0
  struct msg_reachable_change *nmsg = (void *)buf;
651
0
  const uint insz = sizeof(*nmsg->router_ids);
652
0
  const uint nmax = (sizeof(buf) - sizeof(*nmsg)) / insz;
653
0
  uint len;
654
655
0
  if (nadd > nmax)
656
0
    nadd = nmax;
657
0
  if (nremove > (nmax - nadd))
658
0
    nremove = (nmax - nadd);
659
660
0
  if (nadd)
661
0
    memcpy(nmsg->router_ids, add, nadd * insz);
662
0
  if (nremove)
663
0
    memcpy(&nmsg->router_ids[nadd], remove, nremove * insz);
664
665
0
  nmsg->nadd = htons(nadd);
666
0
  nmsg->nremove = htons(nremove);
667
0
  len = sizeof(*nmsg) + insz * (nadd + nremove);
668
669
0
  return msg_new(MSG_REACHABLE_CHANGE, nmsg, seqnum, len);
670
0
}
671
672
struct msg *new_msg_router_id_change(uint32_t seqnum, struct in_addr router_id)
673
0
{
674
0
  struct msg_router_id_change rmsg = {.router_id = router_id};
675
676
0
  return msg_new(MSG_ROUTER_ID_CHANGE, &rmsg, seqnum,
677
0
           sizeof(struct msg_router_id_change));
678
0
}
679
680
#endif /* SUPPORT_OSPF_API */