packet.c 9.29 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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88

  switch(ifa->autype)
  {
    case OSPF_AUTH_NONE:
    case OSPF_AUTH_SIMPLE:
      pkt->checksum = ipsum_calculate(pkt, sizeof(struct ospf_packet) - 8,
				  (pkt + 1),
				  ntohs(pkt->length) -
				  sizeof(struct ospf_packet), NULL);
      password_cpy(pkt->u.password, passwd->password, 8);
      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");
  }
89 90
}

91
static int
92
ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_packet *pkt, int size)
93 94
{
  int i;
95 96 97 98 99 100 101 102 103 104
  struct ospf_iface *ifa = n->ifa;
  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
105 106
  if (pkt->autype != htons(ifa->autype))
  {
107 108
    OSPF_TRACE(D_PACKETS, "OSPF_auth: Method differs (%d)", ntohs(pkt->autype));
    return 0;
Ondřej Filip's avatar
Ondřej Filip committed
109
  }
110

111 112 113 114 115 116 117 118 119 120 121 122
  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;
      }
123

124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 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
      if (memcmp(pkt->u.password,pass, 8))
      {
        OSPF_TRACE(D_PACKETS, "OSPF_auth: different passwords");
	return 0;
      }

      else
        return 1;
      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;
  }
194 195
}

Ondřej Filip's avatar
Ondřej Filip committed
196 197
/**
 * ospf_rx_hook
198
 * @sk: socket we received the packet. Its ignored.
Ondřej Filip's avatar
Ondřej Filip committed
199 200
 * @size: size of the packet
 *
201 202
 * 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
203 204
 * non generic functions.
 */
205
int
Ondřej Filip's avatar
Ondřej Filip committed
206
ospf_rx_hook(sock * sk, int size)
207 208
{
  struct ospf_packet *ps;
209 210
  struct ospf_iface *ifa = (struct ospf_iface *) (sk->data);
  struct proto *p = (struct proto *) (ifa->proto);
211
  struct ospf_neighbor *n;
212
  int osize;
213
  char *mesg = "Bad OSPF packet from ";
214

215 216 217
  if (ifa->stub)
    return (1);

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

Ondřej Filip's avatar
Ondřej Filip committed
220
  ps = (struct ospf_packet *) ipv4_skip_header(sk->rbuf, &size);
221
  osize = ntohs(ps->length);
222
  if (ps == NULL)
Ondřej Filip's avatar
Ondřej Filip committed
223
  {
224 225
    log(L_ERR "%s%I - bad IP header", mesg, sk->faddr);
    return 1;
Ondřej Filip's avatar
Ondřej Filip committed
226
  }
227

Ondřej Filip's avatar
Ondřej Filip committed
228 229
  if ((unsigned) size < sizeof(struct ospf_packet))
  {
230 231
    log(L_ERR "%s%I - too short (%u bytes)", mesg, sk->faddr, size);
    return 1;
Ondřej Filip's avatar
Ondřej Filip committed
232
  }
233

234
  if ((osize > size) || (osize != (4 * (osize / 4))))
Ondřej Filip's avatar
Ondřej Filip committed
235
  {
236
    log(L_ERR "%s%I - size field does not match (%d/%d)", mesg, sk->faddr, ntohs(ps->length), size );
237
    return 1;
Ondřej Filip's avatar
Ondřej Filip committed
238
  }
239 240

  if (ps->version != OSPF_VERSION)
Ondřej Filip's avatar
Ondřej Filip committed
241
  {
242 243
    log(L_ERR "%s%I - version %u", mesg, sk->faddr, ps->version);
    return 1;
Ondřej Filip's avatar
Ondřej Filip committed
244
  }
245

246 247
  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
248
  {
249 250
    log(L_ERR "%s%I - bad checksum", mesg, sk->faddr);
    return 1;
Ondřej Filip's avatar
Ondřej Filip committed
251
  }
252

Ondřej Filip's avatar
Ondřej Filip committed
253 254
  if (ntohl(ps->areaid) != ifa->an)
  {
255 256
    log(L_ERR "%s%I - other area %ld", mesg, sk->faddr, ps->areaid);
    return 1;
Ondřej Filip's avatar
Ondřej Filip committed
257
  }
258

Ondřej Filip's avatar
Ondřej Filip committed
259 260
  if (ntohl(ps->routerid) == p->cf->global->router_id)
  {
261 262
    log(L_ERR "%s%I - received my own router ID!", mesg, sk->faddr);
    return 1;
Ondřej Filip's avatar
Ondřej Filip committed
263
  }
264

Ondřej Filip's avatar
Ondřej Filip committed
265 266
  if (ntohl(ps->routerid) == 0)
  {
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
    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
283
  }
284

285 286 287 288 289 290
  if (!ospf_pkt_checkauth(n, ps, size))
  {
    log(L_ERR "%s%I - authentification failed", mesg, sk->faddr);
    return 1;
  }

Ondřej Filip's avatar
Ondřej Filip committed
291
  /* Dump packet 
292 293 294 295 296 297 298 299
     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
300 301 302
  {
  case HELLO_P:
    DBG("%s: Hello received.\n", p->name);
303
    ospf_hello_receive((struct ospf_hello_packet *) ps, ifa, n, sk->faddr);
Ondřej Filip's avatar
Ondřej Filip committed
304 305 306
    break;
  case DBDES_P:
    DBG("%s: Database description received.\n", p->name);
307
    ospf_dbdes_receive((struct ospf_dbdes_packet *) ps, ifa, n);
Ondřej Filip's avatar
Ondřej Filip committed
308 309 310
    break;
  case LSREQ_P:
    DBG("%s: Link state request received.\n", p->name);
311
    ospf_lsreq_receive((struct ospf_lsreq_packet *) ps, ifa, n);
Ondřej Filip's avatar
Ondřej Filip committed
312 313 314
    break;
  case LSUPD_P:
    DBG("%s: Link state update received.\n", p->name);
315
    ospf_lsupd_receive((struct ospf_lsupd_packet *) ps, ifa, n);
Ondřej Filip's avatar
Ondřej Filip committed
316 317 318
    break;
  case LSACK_P:
    DBG("%s: Link state ack received.\n", p->name);
319
    ospf_lsack_receive((struct ospf_lsack_packet *) ps, ifa, n);
Ondřej Filip's avatar
Ondřej Filip committed
320 321
    break;
  default:
322 323
    log(L_ERR "%s%I - wrong type %u", mesg, sk->faddr, ps->type);
    return 1;
Ondřej Filip's avatar
Ondřej Filip committed
324
  };
325
  return 1;
326 327 328
}

void
Ondřej Filip's avatar
Ondřej Filip committed
329
ospf_tx_hook(sock * sk)
330 331 332 333
{
  struct ospf_iface *ifa;
  struct proto *p;

334
  ifa = (struct ospf_iface *) (sk->data);
335

336
  p = (struct proto *) (ifa->proto);
Ondřej Filip's avatar
Ondřej Filip committed
337
  DBG("%s: TX_Hook called on interface %s\n", p->name, sk->iface->name);
338 339 340
}

void
Ondřej Filip's avatar
Ondřej Filip committed
341
ospf_err_hook(sock * sk, int err UNUSED)
342 343 344 345
{
  struct ospf_iface *ifa;
  struct proto *p;

346
  ifa = (struct ospf_iface *) (sk->data);
347

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

352
void
353
ospf_send_to_agt(sock * sk, struct ospf_iface *ifa, u8 state)
354 355 356
{
  struct ospf_neighbor *n;

Ondřej Filip's avatar
Ondřej Filip committed
357
  WALK_LIST(NODE n, ifa->neigh_list) if (n->state >= state)
358
    ospf_send_to(sk, n->ip, ifa);
359
}
360 361

void
362
ospf_send_to_bdr(sock * sk, struct ospf_iface *ifa)
363
{
Ondřej Filip's avatar
Ondřej Filip committed
364
  if (ipa_compare(ifa->drip, ipa_from_u32(0)) != 0)
365
    ospf_send_to(sk, ifa->drip, ifa);
Ondřej Filip's avatar
Ondřej Filip committed
366
  if (ipa_compare(ifa->bdrip, ipa_from_u32(0)) != 0)
367
    ospf_send_to(sk, ifa->bdrip, ifa);
368
}
Ondřej Filip's avatar
Ondřej Filip committed
369 370

void
371
ospf_send_to(sock *sk, ip_addr ip, struct ospf_iface *ifa)
Ondřej Filip's avatar
Ondřej Filip committed
372
{
373 374 375 376 377 378 379 380
  struct ospf_packet *pkt = (struct ospf_packet *) sk->tbuf;
  int len = ntohs(pkt->length) + (pkt->autype == OSPF_AUTH_CRYPT) ? OSPF_AUTH_CRYPT_SIZE : 0;
  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
381 382
}