packet.c 9.95 KB
Newer Older
1 2 3
/*
 *	BIRD -- OSPF
 *
4
 *	(c) 1999--2004 Ondrej Filip <feela@network.cz>
5 6 7 8 9
 *
 *	Can be freely distributed and used under the terms of the GNU GPL.
 */

#include "ospf.h"
10 11
#include "nest/password.h"
#include "lib/md5.h"
12 13

void
14
ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type)
15 16 17 18
{
  struct ospf_packet *pkt;
  struct proto *p;

19
  p = (struct proto *) (ifa->proto);
20

21
  pkt = (struct ospf_packet *) buf;
22

23
  pkt->version = OSPF_VERSION;
24

25 26
  pkt->type = h_type;

Ondřej Filip's avatar
Ondřej Filip committed
27 28 29
  pkt->routerid = htonl(p->cf->global->router_id);
  pkt->areaid = htonl(ifa->an);
  pkt->autype = htons(ifa->autype);
30
  pkt->checksum = 0;
31 32
}

33 34 35 36 37 38 39
unsigned
ospf_pkt_maxsize(struct ospf_iface *ifa)
{
  return ifa->iface->mtu - SIZE_OF_IP_HEADER -
    ((ifa->autype == OSPF_AUTH_CRYPT) ? OSPF_AUTH_CRYPT_SIZE : 0);
}

40
void
41
ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt)
42
{
43 44 45 46 47 48 49
  struct proto_ospf *po = ifa->proto;
  struct proto *p = &po->proto;
  struct password_item *passwd = password_find (ifa->passwords);
  void *tail;
  struct MD5Context ctxt;
  char password[OSPF_AUTH_CRYPT_SIZE];

Ondřej Filip's avatar
Ondřej Filip committed
50
  pkt->autype = htons(ifa->autype);
51 52 53 54

  switch(ifa->autype)
  {
    case OSPF_AUTH_SIMPLE:
55 56 57 58 59 60 61
      bzero(&pkt->u, sizeof(union ospf_auth));
      if (!passwd)
      {
        log( L_ERR "No suitable password found for authentication" );
        return;
      }
      password_cpy(pkt->u.password, passwd->password, sizeof(union ospf_auth));
62
    case OSPF_AUTH_NONE:
63 64
      pkt->checksum = ipsum_calculate(pkt, sizeof(struct ospf_packet) -
                                  sizeof(union ospf_auth), (pkt + 1),
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
				  ntohs(pkt->length) -
				  sizeof(struct ospf_packet), NULL);
      break;
    case OSPF_AUTH_CRYPT:
      if (!passwd)
      {
        log( L_ERR "No suitable password found for authentication" );
        return;
      }

      pkt->checksum = 0;

      if (!ifa->csn)
        ifa->csn = (u32) time(NULL);

      pkt->u.md5.keyid = passwd->id;
      pkt->u.md5.len = OSPF_AUTH_CRYPT_SIZE;
      pkt->u.md5.zero = 0;
      pkt->u.md5.csn = htonl(ifa->csn++);
      tail = ((void *)pkt) + ntohs(pkt->length);
      MD5Init(&ctxt);
      MD5Update(&ctxt, (char *) pkt, ntohs(pkt->length));
      password_cpy(password, passwd->password, OSPF_AUTH_CRYPT_SIZE);
      MD5Update(&ctxt, password, OSPF_AUTH_CRYPT_SIZE);
      MD5Final(tail, &ctxt);
      
      break;
    default:
      bug("Unknown authentication type");
  }
95 96
}

97
static int
98
ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_packet *pkt, int size)
99 100
{
  int i;
101 102 103 104 105 106 107 108 109
  struct proto_ospf *po = ifa->proto;
  struct proto *p = &po->proto;
  struct password_item *pass = NULL, *ptmp;
  void *tail;
  char md5sum[OSPF_AUTH_CRYPT_SIZE];
  char password[OSPF_AUTH_CRYPT_SIZE];
  struct MD5Context ctxt;


Ondřej Filip's avatar
Ondřej Filip committed
110 111
  if (pkt->autype != htons(ifa->autype))
  {
112 113
    OSPF_TRACE(D_PACKETS, "OSPF_auth: Method differs (%d)", ntohs(pkt->autype));
    return 0;
Ondřej Filip's avatar
Ondřej Filip committed
114
  }
115

116 117 118 119 120 121 122
  if (n && (ifa != n->ifa))
  {
    OSPF_TRACE(D_PACKETS, "OSPF_auth: received packet from strange interface (%s/%s)",
      ifa->iface->name, n->ifa->iface->name);
    return 0;
  }

123 124 125 126 127 128 129 130 131 132 133 134
  switch(ifa->autype)
  {
    case OSPF_AUTH_NONE:
      return 1;
      break;
    case OSPF_AUTH_SIMPLE:
      pass = password_find (ifa->passwords);
      if(!pass)
      {
        OSPF_TRACE(D_PACKETS, "OSPF_auth: no password found");
	return 0;
      }
135
      password_cpy(password, pass->password, sizeof(union ospf_auth));
136

137
      if (memcmp(pkt->u.password, password, sizeof(union ospf_auth)))
138
      {
139 140 141 142
        char ppass[sizeof(union ospf_auth) + 1];
        bzero(ppass, (sizeof(union ospf_auth) + 1));
        memcpy(ppass, pkt->u.password, sizeof(union ospf_auth));
        OSPF_TRACE(D_PACKETS, "OSPF_auth: different passwords (%s)", ppass);
143 144
	return 0;
      }
145
      return 1;
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
      break;
    case OSPF_AUTH_CRYPT:
      if (pkt->u.md5.len != OSPF_AUTH_CRYPT_SIZE)
      {
        OSPF_TRACE(D_PACKETS, "OSPF_auth: wrong size of md5 digest");
        return 0;
      }
      if (ntohs(pkt->length) + OSPF_AUTH_CRYPT_SIZE != size)
      {
        OSPF_TRACE(D_PACKETS, "OSPF_auth: size mismatch");
        return 0;
      }

      if (pkt->u.md5.zero)
      {
        OSPF_TRACE(D_PACKETS, "OSPF_auth: \"zero\" area is non-zero");
        return 0;
      }

      tail = ((void *)pkt) + ntohs(pkt->length);

      WALK_LIST(ptmp, *(ifa->passwords))
      {
        if (pkt->u.md5.keyid != pass->id) continue;
        if ((pass->genfrom > now) || (pass->gento < now)) continue;
        pass = ptmp;
        break;
      }

      if(!pass)
      {
        OSPF_TRACE(D_PACKETS, "OSPF_auth: no suitable md5 password found");
        return 0;
      }

      if(n)
      {
        if(ntohs(pkt->u.md5.csn) <= n->csn)
        {
          OSPF_TRACE(D_PACKETS, "OSPF_auth: lower sequence number");
          return 0;
        }

        n->csn = ntohs(pkt->u.md5.csn);
      }

      MD5Init(&ctxt);
      MD5Update(&ctxt, (char *) pkt, ntohs(pkt->length));
      password_cpy(password, pass->password, OSPF_AUTH_CRYPT_SIZE);
      MD5Update(&ctxt, password, OSPF_AUTH_CRYPT_SIZE);
      MD5Final(md5sum, &ctxt);
      if (!memcmp(md5sum, tail, OSPF_AUTH_CRYPT_SIZE))
      {
        OSPF_TRACE(D_PACKETS, "OSPF_auth: wrong md5 digest");
        return 0;
      }
      return 1;
      break;
    default:
      OSPF_TRACE(D_PACKETS, "OSPF_auth: unknown auth type");
      return 0;
  }
208 209
}

Ondřej Filip's avatar
Ondřej Filip committed
210 211
/**
 * ospf_rx_hook
212
 * @sk: socket we received the packet. Its ignored.
Ondřej Filip's avatar
Ondřej Filip committed
213 214
 * @size: size of the packet
 *
215 216
 * This is the entry point for messages from neighbors. Many checks (like
 * authentication, checksums, size) are done before the packet is passed to
Ondřej Filip's avatar
Ondřej Filip committed
217 218
 * non generic functions.
 */
219
int
Ondřej Filip's avatar
Ondřej Filip committed
220
ospf_rx_hook(sock * sk, int size)
221 222
{
  struct ospf_packet *ps;
223 224
  struct ospf_iface *ifa = (struct ospf_iface *) (sk->data);
  struct proto *p = (struct proto *) (ifa->proto);
225
  struct ospf_neighbor *n;
226
  int osize;
227
  char *mesg = "Bad OSPF packet from ";
228

229 230 231
  if (ifa->stub)
    return (1);

Ondřej Filip's avatar
Ondřej Filip committed
232
  DBG("%s: RX_Hook called on interface %s.\n", p->name, sk->iface->name);
233

Ondřej Filip's avatar
Ondřej Filip committed
234
  ps = (struct ospf_packet *) ipv4_skip_header(sk->rbuf, &size);
235
  osize = ntohs(ps->length);
236
  if (ps == NULL)
Ondřej Filip's avatar
Ondřej Filip committed
237
  {
238 239
    log(L_ERR "%s%I - bad IP header", mesg, sk->faddr);
    return 1;
Ondřej Filip's avatar
Ondřej Filip committed
240
  }
241

Ondřej Filip's avatar
Ondřej Filip committed
242 243
  if ((unsigned) size < sizeof(struct ospf_packet))
  {
244 245
    log(L_ERR "%s%I - too short (%u bytes)", mesg, sk->faddr, size);
    return 1;
Ondřej Filip's avatar
Ondřej Filip committed
246
  }
247

248
  if ((osize > size) || (osize != (4 * (osize / 4))))
Ondřej Filip's avatar
Ondřej Filip committed
249
  {
250
    log(L_ERR "%s%I - size field does not match (%d/%d)", mesg, sk->faddr, ntohs(ps->length), size );
251
    return 1;
Ondřej Filip's avatar
Ondřej Filip committed
252
  }
253 254

  if (ps->version != OSPF_VERSION)
Ondřej Filip's avatar
Ondřej Filip committed
255
  {
256 257
    log(L_ERR "%s%I - version %u", mesg, sk->faddr, ps->version);
    return 1;
Ondřej Filip's avatar
Ondřej Filip committed
258
  }
259

260 261
  if ((ifa->autype != OSPF_AUTH_CRYPT) && (!ipsum_verify(ps, 16, (void *) ps + sizeof(struct ospf_packet),
		    ntohs(ps->length) - sizeof(struct ospf_packet), NULL)))
Ondřej Filip's avatar
Ondřej Filip committed
262
  {
263 264
    log(L_ERR "%s%I - bad checksum", mesg, sk->faddr);
    return 1;
Ondřej Filip's avatar
Ondřej Filip committed
265
  }
266

Ondřej Filip's avatar
Ondřej Filip committed
267 268
  if (ntohl(ps->areaid) != ifa->an)
  {
269 270
    log(L_ERR "%s%I - other area %ld", mesg, sk->faddr, ps->areaid);
    return 1;
Ondřej Filip's avatar
Ondřej Filip committed
271
  }
272

Ondřej Filip's avatar
Ondřej Filip committed
273 274
  if (ntohl(ps->routerid) == p->cf->global->router_id)
  {
275 276
    log(L_ERR "%s%I - received my own router ID!", mesg, sk->faddr);
    return 1;
Ondřej Filip's avatar
Ondřej Filip committed
277
  }
278

Ondřej Filip's avatar
Ondřej Filip committed
279 280
  if (ntohl(ps->routerid) == 0)
  {
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
    log(L_ERR "%s%I - router id = 0.0.0.0", mesg, sk->faddr);
    return 1;
  }

  if ((unsigned) size > ifa->iface->mtu)
  {
    log(L_ERR "%s%I - received larger packet than MTU", mesg, sk->faddr);
    return 1;
  }

  n = find_neigh(ifa, ntohl(((struct ospf_packet *) ps)->routerid));

  if(!n && (ps->type != HELLO_P))
  {
    OSPF_TRACE(D_PACKETS, "Received non-hello packet from uknown neighbor (%I)", sk->faddr);
    return 1;
Ondřej Filip's avatar
Ondřej Filip committed
297
  }
298

299
  if (!ospf_pkt_checkauth(n, ifa, ps, size))
300 301 302 303 304
  {
    log(L_ERR "%s%I - authentification failed", mesg, sk->faddr);
    return 1;
  }

Ondřej Filip's avatar
Ondřej Filip committed
305
  /* Dump packet 
306 307 308 309 310 311 312 313
     pu8=(u8 *)(sk->rbuf+5*4);
     for(i=0;i<ntohs(ps->length);i+=4)
     DBG("%s: received %u,%u,%u,%u\n",p->name, pu8[i+0], pu8[i+1], pu8[i+2],
     pu8[i+3]);
     DBG("%s: received size: %u\n",p->name,size);
   */

  switch (ps->type)
Ondřej Filip's avatar
Ondřej Filip committed
314 315 316
  {
  case HELLO_P:
    DBG("%s: Hello received.\n", p->name);
317
    ospf_hello_receive((struct ospf_hello_packet *) ps, ifa, n, sk->faddr);
Ondřej Filip's avatar
Ondřej Filip committed
318 319 320
    break;
  case DBDES_P:
    DBG("%s: Database description received.\n", p->name);
321
    ospf_dbdes_receive((struct ospf_dbdes_packet *) ps, ifa, n);
Ondřej Filip's avatar
Ondřej Filip committed
322 323 324
    break;
  case LSREQ_P:
    DBG("%s: Link state request received.\n", p->name);
325
    ospf_lsreq_receive((struct ospf_lsreq_packet *) ps, ifa, n);
Ondřej Filip's avatar
Ondřej Filip committed
326 327 328
    break;
  case LSUPD_P:
    DBG("%s: Link state update received.\n", p->name);
329
    ospf_lsupd_receive((struct ospf_lsupd_packet *) ps, ifa, n);
Ondřej Filip's avatar
Ondřej Filip committed
330 331 332
    break;
  case LSACK_P:
    DBG("%s: Link state ack received.\n", p->name);
333
    ospf_lsack_receive((struct ospf_lsack_packet *) ps, ifa, n);
Ondřej Filip's avatar
Ondřej Filip committed
334 335
    break;
  default:
336 337
    log(L_ERR "%s%I - wrong type %u", mesg, sk->faddr, ps->type);
    return 1;
Ondřej Filip's avatar
Ondřej Filip committed
338
  };
339
  return 1;
340 341 342
}

void
Ondřej Filip's avatar
Ondřej Filip committed
343
ospf_tx_hook(sock * sk)
344 345 346 347
{
  struct ospf_iface *ifa;
  struct proto *p;

348
  ifa = (struct ospf_iface *) (sk->data);
349

350
  p = (struct proto *) (ifa->proto);
Ondřej Filip's avatar
Ondřej Filip committed
351
  DBG("%s: TX_Hook called on interface %s\n", p->name, sk->iface->name);
352 353 354
}

void
Ondřej Filip's avatar
Ondřej Filip committed
355
ospf_err_hook(sock * sk, int err UNUSED)
356 357 358 359
{
  struct ospf_iface *ifa;
  struct proto *p;

360
  ifa = (struct ospf_iface *) (sk->data);
361

362
  p = (struct proto *) (ifa->proto);
Ondřej Filip's avatar
Ondřej Filip committed
363
  DBG("%s: Err_Hook called on interface %s\n", p->name, sk->iface->name);
364 365
}

366
void
367
ospf_send_to_agt(sock * sk, struct ospf_iface *ifa, u8 state)
368 369 370
{
  struct ospf_neighbor *n;

Ondřej Filip's avatar
Ondřej Filip committed
371
  WALK_LIST(NODE n, ifa->neigh_list) if (n->state >= state)
372
    ospf_send_to(sk, n->ip, ifa);
373
}
374 375

void
376
ospf_send_to_bdr(sock * sk, struct ospf_iface *ifa)
377
{
Ondřej Filip's avatar
Ondřej Filip committed
378
  if (ipa_compare(ifa->drip, ipa_from_u32(0)) != 0)
379
    ospf_send_to(sk, ifa->drip, ifa);
Ondřej Filip's avatar
Ondřej Filip committed
380
  if (ipa_compare(ifa->bdrip, ipa_from_u32(0)) != 0)
381
    ospf_send_to(sk, ifa->bdrip, ifa);
382
}
Ondřej Filip's avatar
Ondřej Filip committed
383 384

void
385
ospf_send_to(sock *sk, ip_addr ip, struct ospf_iface *ifa)
Ondřej Filip's avatar
Ondřej Filip committed
386
{
387
  struct ospf_packet *pkt = (struct ospf_packet *) sk->tbuf;
388
  int len = ntohs(pkt->length) + ((ifa->autype == OSPF_AUTH_CRYPT) ? OSPF_AUTH_CRYPT_SIZE : 0);
389 390 391 392 393 394
  ospf_pkt_finalize(ifa, pkt);

  if (ipa_equal(ip, IPA_NONE))
    sk_send(sk, len);
  else
    sk_send_to(sk, len, ip, OSPF_PROTO);
Ondřej Filip's avatar
Ondřej Filip committed
395 396
}