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

#include "ospf.h"

Ondřej Filip's avatar
Ondřej Filip committed
11
void
Ondřej Filip's avatar
Ondřej Filip committed
12
flush_lsa(struct top_hash_entry *en, struct ospf_area *oa)
Ondřej Filip's avatar
Ondřej Filip committed
13
{
Ondřej Filip's avatar
Ondřej Filip committed
14 15 16 17
  struct proto *p = &oa->po->proto;
  OSPF_TRACE(D_EVENTS,
	     "Going to remove node Type: %u, Id: %I, Rt: %I, Age: %u",
	     en->lsa.type, en->lsa.id, en->lsa.rt, en->lsa.age);
Ondřej Filip's avatar
Ondřej Filip committed
18
  s_rem_node(SNODE en);
Ondřej Filip's avatar
Ondřej Filip committed
19 20 21 22
  if (en->lsa_body != NULL)
    mb_free(en->lsa_body);
  en->lsa_body = NULL;
  ospf_hash_delete(oa->gr, en);
Ondřej Filip's avatar
Ondřej Filip committed
23 24
}

25 26 27 28
/**
 * ospf_age
 * @oa: ospf area
 *
29 30 31 32
 * This function is periodicaly invoked from area_disp(). It computes the new
 * age of all LSAs and old (@age is higher than %LSA_MAXAGE) LSAs are flushed
 * whenever possible. If an LSA originated by the router itself is older
 * than %LSREFRESHTIME a new instance is originated.
33
 *
34 35
 * The RFC says that a router should check the checksum of every LSA to detect
 * hardware problems. BIRD does not do this to minimalize CPU utilization.
36
 *
37
 * If routing table calculation is scheduled, it also invalidates the old routing
38
 * table calculation results.
39
 */
Ondřej Filip's avatar
Ondřej Filip committed
40
void
Ondřej Filip's avatar
Ondřej Filip committed
41
ospf_age(struct ospf_area *oa)
Ondřej Filip's avatar
Ondřej Filip committed
42
{
Ondřej Filip's avatar
Ondřej Filip committed
43 44 45 46
  struct proto *p = &oa->po->proto;
  struct proto_ospf *po = (struct proto_ospf *) p;
  struct top_hash_entry *en, *nxt;
  int flush = can_flush_lsa(oa);
Ondřej Filip's avatar
Ondřej Filip committed
47
  int cleanup = (oa->rt && (oa->rt->dist != LSINFINITY));
Ondřej Filip's avatar
Ondřej Filip committed
48

Ondřej Filip's avatar
Ondřej Filip committed
49 50
  OSPF_TRACE(D_EVENTS, "Running ospf_age");

Ondřej Filip's avatar
Ondřej Filip committed
51
  WALK_SLIST_DELSAFE(en, nxt, oa->lsal)
Ondřej Filip's avatar
Ondřej Filip committed
52
  {
Ondřej Filip's avatar
Ondřej Filip committed
53
    if (cleanup)
54
    {
Ondřej Filip's avatar
Ondřej Filip committed
55 56 57 58
      en->color = OUTSPF;
      en->dist = LSINFINITY;
      en->nhi = NULL;
      en->nh = ipa_from_u32(0);
59
      DBG("Infinitying Type: %u, Id: %I, Rt: %I\n", en->lsa.type, en->lsa.id,
Ondřej Filip's avatar
Ondřej Filip committed
60
	  en->lsa.rt);
61
    }
Ondřej Filip's avatar
Ondřej Filip committed
62
    if (en->lsa.age == LSA_MAXAGE)
Ondřej Filip's avatar
Ondřej Filip committed
63
    {
Ondřej Filip's avatar
Ondřej Filip committed
64 65
      if (flush)
	flush_lsa(en, oa);
Ondřej Filip's avatar
Ondřej Filip committed
66
      continue;
Ondřej Filip's avatar
Ondřej Filip committed
67
    }
Ondřej Filip's avatar
Ondřej Filip committed
68 69
    if ((en->lsa.rt == p->cf->global->router_id) &&(en->lsa.age >=
						    LSREFRESHTIME))
Ondřej Filip's avatar
Ondřej Filip committed
70
    {
Ondřej Filip's avatar
Ondřej Filip committed
71 72 73 74 75 76
      OSPF_TRACE(D_EVENTS, "Refreshing my LSA: Type: %u, Id: %I, Rt: %I",
		 en->lsa.type, en->lsa.id, en->lsa.rt);
      en->lsa.sn++;
      en->lsa.age = 0;
      en->inst_t = now;
      en->ini_age = 0;
Ondřej Filip's avatar
Ondřej Filip committed
77
      lsasum_calculate(&en->lsa, en->lsa_body);
Ondřej Filip's avatar
Ondřej Filip committed
78 79
      ospf_lsupd_flood(NULL, NULL, &en->lsa, NULL, oa, 1);
      continue;
Ondřej Filip's avatar
Ondřej Filip committed
80
    }
Ondřej Filip's avatar
Ondřej Filip committed
81
    if ((en->lsa.age = (en->ini_age + (now - en->inst_t))) >= LSA_MAXAGE)
Ondřej Filip's avatar
Ondřej Filip committed
82
    {
Ondřej Filip's avatar
Ondřej Filip committed
83
      if (flush)
84
      {
Ondřej Filip's avatar
Ondřej Filip committed
85
	flush_lsa(en, oa);
86
	schedule_rtcalc(po);
87
      }
Ondřej Filip's avatar
Ondřej Filip committed
88 89
      else
	en->lsa.age = LSA_MAXAGE;
Ondřej Filip's avatar
Ondřej Filip committed
90
    }
Ondřej Filip's avatar
Ondřej Filip committed
91 92 93
  }
}

94 95 96
void
htonlsah(struct ospf_lsa_header *h, struct ospf_lsa_header *n)
{
Ondřej Filip's avatar
Ondřej Filip committed
97 98 99 100 101 102 103 104
  n->age = htons(h->age);
  n->options = h->options;
  n->type = h->type;
  n->id = htonl(h->id);
  n->rt = htonl(h->rt);
  n->sn = htonl(h->sn);
  n->checksum = htons(h->checksum);
  n->length = htons(h->length);
105 106 107 108 109
};

void
ntohlsah(struct ospf_lsa_header *n, struct ospf_lsa_header *h)
{
Ondřej Filip's avatar
Ondřej Filip committed
110 111 112 113 114 115 116 117
  h->age = ntohs(n->age);
  h->options = n->options;
  h->type = n->type;
  h->id = ntohl(n->id);
  h->rt = ntohl(n->rt);
  h->sn = ntohl(n->sn);
  h->checksum = ntohs(n->checksum);
  h->length = ntohs(n->length);
118 119 120 121 122 123
};

void
htonlsab(void *h, void *n, u8 type, u16 len)
{
  unsigned int i;
Ondřej Filip's avatar
Ondřej Filip committed
124
  switch (type)
125
  {
Ondřej Filip's avatar
Ondřej Filip committed
126
  case LSA_T_RT:
127 128
    {
      struct ospf_lsa_rt *hrt, *nrt;
Ondřej Filip's avatar
Ondřej Filip committed
129
      struct ospf_lsa_rt_link *hrtl, *nrtl;
130
      u16 links;
131

Ondřej Filip's avatar
Ondřej Filip committed
132 133 134
      nrt = n;
      hrt = h;
      links = hrt->links;
135

Ondřej Filip's avatar
Ondřej Filip committed
136 137 138 139 140 141
      nrt->veb.byte = hrt->veb.byte;
      nrt->padding = 0;
      nrt->links = htons(hrt->links);
      nrtl = (struct ospf_lsa_rt_link *) (nrt + 1);
      hrtl = (struct ospf_lsa_rt_link *) (hrt + 1);
      for (i = 0; i < links; i++)
142
      {
Ondřej Filip's avatar
Ondřej Filip committed
143 144 145 146 147
	(nrtl + i)->id = htonl((hrtl + i)->id);
	(nrtl + i)->data = htonl((hrtl + i)->data);
	(nrtl + i)->type = (hrtl + i)->type;
	(nrtl + i)->notos = (hrtl + i)->notos;
	(nrtl + i)->metric = htons((hrtl + i)->metric);
148 149 150
      }
      break;
    }
Ondřej Filip's avatar
Ondřej Filip committed
151
  case LSA_T_NET:
152
    {
Ondřej Filip's avatar
Ondřej Filip committed
153
      u32 *hid, *nid;
154

Ondřej Filip's avatar
Ondřej Filip committed
155 156
      nid = n;
      hid = h;
157

Ondřej Filip's avatar
Ondřej Filip committed
158
      for (i = 0; i < (len / sizeof(u32)); i++)
159
      {
Ondřej Filip's avatar
Ondřej Filip committed
160
	*(nid + i) = htonl(*(hid + i));
161 162 163
      }
      break;
    }
Ondřej Filip's avatar
Ondřej Filip committed
164 165
  case LSA_T_SUM_NET:
  case LSA_T_SUM_RT:
166 167 168 169
    {
      struct ospf_lsa_summ *hs, *ns;
      struct ospf_lsa_summ_net *hn, *nn;

Ondřej Filip's avatar
Ondřej Filip committed
170 171
      hs = h;
      ns = n;
172

Ondřej Filip's avatar
Ondřej Filip committed
173
      ns->netmask = hs->netmask;
174
      ipa_hton(ns->netmask);
175

Ondřej Filip's avatar
Ondřej Filip committed
176 177
      hn = (struct ospf_lsa_summ_net *) (hs + 1);
      nn = (struct ospf_lsa_summ_net *) (ns + 1);
178

Ondřej Filip's avatar
Ondřej Filip committed
179 180
      for (i = 0; i < ((len - sizeof(struct ospf_lsa_summ)) /
		       sizeof(struct ospf_lsa_summ_net)); i++)
181
      {
Ondřej Filip's avatar
Ondřej Filip committed
182 183 184
	(nn + i)->tos = (hn + i)->tos;
	(nn + i)->metric = htons((hn + i)->metric);
	(nn + i)->padding = 0;
185 186 187
      }
      break;
    }
Ondřej Filip's avatar
Ondřej Filip committed
188
  case LSA_T_EXT:
189 190 191 192
    {
      struct ospf_lsa_ext *he, *ne;
      struct ospf_lsa_ext_tos *ht, *nt;

Ondřej Filip's avatar
Ondřej Filip committed
193 194
      he = h;
      ne = n;
195

Ondřej Filip's avatar
Ondřej Filip committed
196
      ne->netmask = he->netmask;
197
      ipa_hton(ne->netmask);
198

Ondřej Filip's avatar
Ondřej Filip committed
199 200
      ht = (struct ospf_lsa_ext_tos *) (he + 1);
      nt = (struct ospf_lsa_ext_tos *) (ne + 1);
201

Ondřej Filip's avatar
Ondřej Filip committed
202 203
      for (i = 0; i < ((len - sizeof(struct ospf_lsa_ext)) /
		       sizeof(struct ospf_lsa_ext_tos)); i++)
204
      {
Ondřej Filip's avatar
Ondřej Filip committed
205 206 207 208 209 210
	(nt + i)->etos = (ht + i)->etos;
	(nt + i)->padding = 0;
	(nt + i)->metric = htons((ht + i)->metric);
	(nt + i)->fwaddr = (ht + i)->fwaddr;
	ipa_hton((nt + i)->fwaddr);
	(nt + i)->tag = htonl((ht + i)->tag);
211 212 213
      }
      break;
    }
Ondřej Filip's avatar
Ondřej Filip committed
214 215
  default:
    bug("(hton): Unknown LSA");
216 217 218 219 220 221 222
  }
};

void
ntohlsab(void *n, void *h, u8 type, u16 len)
{
  unsigned int i;
Ondřej Filip's avatar
Ondřej Filip committed
223
  switch (type)
224
  {
Ondřej Filip's avatar
Ondřej Filip committed
225
  case LSA_T_RT:
226 227
    {
      struct ospf_lsa_rt *hrt, *nrt;
Ondřej Filip's avatar
Ondřej Filip committed
228
      struct ospf_lsa_rt_link *hrtl, *nrtl;
229
      u16 links;
230

Ondřej Filip's avatar
Ondřej Filip committed
231 232
      nrt = n;
      hrt = h;
233

Ondřej Filip's avatar
Ondřej Filip committed
234 235 236 237 238 239
      hrt->veb.byte = nrt->veb.byte;
      hrt->padding = 0;
      links = hrt->links = ntohs(nrt->links);
      nrtl = (struct ospf_lsa_rt_link *) (nrt + 1);
      hrtl = (struct ospf_lsa_rt_link *) (hrt + 1);
      for (i = 0; i < links; i++)
240
      {
Ondřej Filip's avatar
Ondřej Filip committed
241 242 243 244 245
	(hrtl + i)->id = ntohl((nrtl + i)->id);
	(hrtl + i)->data = ntohl((nrtl + i)->data);
	(hrtl + i)->type = (nrtl + i)->type;
	(hrtl + i)->notos = (nrtl + i)->notos;
	(hrtl + i)->metric = ntohs((nrtl + i)->metric);
246 247 248
      }
      break;
    }
Ondřej Filip's avatar
Ondřej Filip committed
249
  case LSA_T_NET:
250
    {
Ondřej Filip's avatar
Ondřej Filip committed
251
      u32 *hid, *nid;
252

Ondřej Filip's avatar
Ondřej Filip committed
253 254
      hid = h;
      nid = n;
255

Ondřej Filip's avatar
Ondřej Filip committed
256
      for (i = 0; i < (len / sizeof(u32)); i++)
257
      {
Ondřej Filip's avatar
Ondřej Filip committed
258
	*(hid + i) = ntohl(*(nid + i));
259 260 261
      }
      break;
    }
Ondřej Filip's avatar
Ondřej Filip committed
262 263
  case LSA_T_SUM_NET:
  case LSA_T_SUM_RT:
264 265 266 267
    {
      struct ospf_lsa_summ *hs, *ns;
      struct ospf_lsa_summ_net *hn, *nn;

Ondřej Filip's avatar
Ondřej Filip committed
268 269
      hs = h;
      ns = n;
270

Ondřej Filip's avatar
Ondřej Filip committed
271
      hs->netmask = ns->netmask;
272
      ipa_ntoh(hs->netmask);
273

Ondřej Filip's avatar
Ondřej Filip committed
274 275
      hn = (struct ospf_lsa_summ_net *) (hs + 1);
      nn = (struct ospf_lsa_summ_net *) (ns + 1);
276

Ondřej Filip's avatar
Ondřej Filip committed
277 278
      for (i = 0; i < ((len - sizeof(struct ospf_lsa_summ)) /
		       sizeof(struct ospf_lsa_summ_net)); i++)
279
      {
Ondřej Filip's avatar
Ondřej Filip committed
280 281 282
	(hn + i)->tos = (nn + i)->tos;
	(hn + i)->metric = ntohs((nn + i)->metric);
	(hn + i)->padding = 0;
283 284 285
      }
      break;
    }
Ondřej Filip's avatar
Ondřej Filip committed
286
  case LSA_T_EXT:
287 288 289 290
    {
      struct ospf_lsa_ext *he, *ne;
      struct ospf_lsa_ext_tos *ht, *nt;

Ondřej Filip's avatar
Ondřej Filip committed
291 292
      he = h;
      ne = n;
293

Ondřej Filip's avatar
Ondřej Filip committed
294
      he->netmask = ne->netmask;
295
      ipa_ntoh(he->netmask);
296

Ondřej Filip's avatar
Ondřej Filip committed
297 298
      ht = (struct ospf_lsa_ext_tos *) (he + 1);
      nt = (struct ospf_lsa_ext_tos *) (ne + 1);
299

Ondřej Filip's avatar
Ondřej Filip committed
300 301
      for (i = 0; i < ((len - sizeof(struct ospf_lsa_ext)) /
		       sizeof(struct ospf_lsa_ext_tos)); i++)
302
      {
Ondřej Filip's avatar
Ondřej Filip committed
303 304 305 306 307 308
	(ht + i)->etos = (nt + i)->etos;
	(ht + i)->padding = 0;
	(ht + i)->metric = ntohs((nt + i)->metric);
	(ht + i)->fwaddr = (nt + i)->fwaddr;
	ipa_ntoh((ht + i)->fwaddr);
	(ht + i)->tag = ntohl((nt + i)->tag);
309 310 311
      }
      break;
    }
Ondřej Filip's avatar
Ondřej Filip committed
312 313
  default:
    bug("(ntoh): Unknown LSA");
314 315 316
  }
};

317 318 319 320 321 322 323 324
#define MODX 4102		/* larges signed value without overflow */

/* Fletcher Checksum -- Refer to RFC1008. */
#define MODX                 4102
#define LSA_CHECKSUM_OFFSET    15

/* FIXME This is VERY uneficient, I have huge endianity problems */
void
Ondřej Filip's avatar
Ondřej Filip committed
325
lsasum_calculate(struct ospf_lsa_header *h, void *body)
326 327 328
{
  u16 length;

Ondřej Filip's avatar
Ondřej Filip committed
329 330 331 332
  length = h->length;

  htonlsah(h, h);
  htonlsab(body, body, h->type, length - sizeof(struct ospf_lsa_header));
333

Ondřej Filip's avatar
Ondřej Filip committed
334
  (void) lsasum_check(h, body);
Ondřej Filip's avatar
Ondřej Filip committed
335 336 337

  ntohlsah(h, h);
  ntohlsab(body, body, h->type, length - sizeof(struct ospf_lsa_header));
338 339 340 341 342 343 344
}

/*
 * Note, that this function expects that LSA is in big endianity
 * It also returns value in big endian
 */
u16
Ondřej Filip's avatar
Ondřej Filip committed
345
lsasum_check(struct ospf_lsa_header *h, void *body)
346 347 348 349
{
  u8 *sp, *ep, *p, *q, *b;
  int c0 = 0, c1 = 0;
  int x, y;
350
  u16 length;
351

352
  b = body;
353
  sp = (char *) &h->options;
Ondřej Filip's avatar
Ondřej Filip committed
354
  length = ntohs(h->length) - 2;
355
  h->checksum = 0;
356 357

  for (ep = sp + length; sp < ep; sp = q)
Ondřej Filip's avatar
Ondřej Filip committed
358
  {				/* Actually MODX is very large, do we need the for-cyclus? */
359
    q = sp + MODX;
Ondřej Filip's avatar
Ondřej Filip committed
360 361
    if (q > ep)
      q = ep;
362
    for (p = sp; p < q; p++)
363
    {
364 365 366 367 368 369
      /* 
       * I count with bytes from header and than from body
       * but if there is no body, it's appended to header
       * (probably checksum in update receiving) and I go on
       * after header
       */
Ondřej Filip's avatar
Ondřej Filip committed
370
      if ((b == NULL) || (p < (u8 *) (h + 1)))
371
      {
Ondřej Filip's avatar
Ondřej Filip committed
372
	c0 += *p;
373 374 375
      }
      else
      {
Ondřej Filip's avatar
Ondřej Filip committed
376
	c0 += *(b + (p - sp) - sizeof(struct ospf_lsa_header) + 2);
377 378 379
      }

      c1 += c0;
380
    }
381 382 383
    c0 %= 255;
    c1 %= 255;
  }
384 385

  x = ((length - LSA_CHECKSUM_OFFSET) * c0 - c1) % 255;
Ondřej Filip's avatar
Ondřej Filip committed
386 387
  if (x <= 0)
    x += 255;
388
  y = 510 - c0 - x;
Ondřej Filip's avatar
Ondřej Filip committed
389 390
  if (y > 255)
    y -= 255;
391

Ondřej Filip's avatar
Ondřej Filip committed
392 393
  ((u8 *) & h->checksum)[0] = x;
  ((u8 *) & h->checksum)[1] = y;
394
  return h->checksum;
395 396
}

Ondřej Filip's avatar
Ondřej Filip committed
397 398
int
lsa_comp(struct ospf_lsa_header *l1, struct ospf_lsa_header *l2)
399
			/* Return codes from point of view of l1 */
Ondřej Filip's avatar
Ondřej Filip committed
400
{
Ondřej Filip's avatar
Ondřej Filip committed
401
  u32 sn1, sn2;
402

Ondřej Filip's avatar
Ondřej Filip committed
403 404
  sn1 = l1->sn - LSA_INITSEQNO + 1;
  sn2 = l2->sn - LSA_INITSEQNO + 1;
405

Ondřej Filip's avatar
Ondřej Filip committed
406 407 408 409
  if (sn1 > sn2)
    return CMP_NEWER;
  if (sn1 < sn2)
    return CMP_OLDER;
Ondřej Filip's avatar
Ondřej Filip committed
410

Ondřej Filip's avatar
Ondřej Filip committed
411 412
  if (l1->checksum != l2->checksum)
    return l1->checksum < l2->checksum ? CMP_OLDER : CMP_NEWER;
Ondřej Filip's avatar
Ondřej Filip committed
413

Ondřej Filip's avatar
Ondřej Filip committed
414 415 416 417
  if ((l1->age == LSA_MAXAGE) && (l2->age != LSA_MAXAGE))
    return CMP_NEWER;
  if ((l2->age == LSA_MAXAGE) && (l1->age != LSA_MAXAGE))
    return CMP_OLDER;
Ondřej Filip's avatar
Ondřej Filip committed
418

Ondřej Filip's avatar
Ondřej Filip committed
419 420
  if (ABS(l1->age - l2->age) > LSA_MAXAGEDIFF)
    return l1->age < l2->age ? CMP_NEWER : CMP_OLDER;
Ondřej Filip's avatar
Ondřej Filip committed
421 422

  return CMP_SAME;
Ondřej Filip's avatar
Ondřej Filip committed
423 424
}

425 426 427
/**
 * lsa_install_new - install new LSA into database
 * @lsa: LSA header
Martin Mareš's avatar
Martin Mareš committed
428
 * @body: pointer to LSA body
429 430 431
 * @oa: current ospf_area
 *
 * This function ensures installing new LSA into LSA database. Old instance is
Ondřej Filip's avatar
Ondřej Filip committed
432
 * replaced. Several actions are taken to detect if new routing table
433 434
 * calculation is necessary. This is described in 13.2 of RFC 2328.
 */
435
struct top_hash_entry *
436
lsa_install_new(struct ospf_lsa_header *lsa, void *body, struct ospf_area *oa)
437
{
438
  /* LSA can be temporarrily, but body must be mb_allocated. */
Ondřej Filip's avatar
Ondřej Filip committed
439
  int change = 0;
440
  unsigned i;
441
  struct top_hash_entry *en;
442
  struct proto_ospf *po = oa->po;
443

Ondřej Filip's avatar
Ondřej Filip committed
444
  if ((en = ospf_hash_find_header(oa->gr, lsa)) == NULL)
445
  {
Ondřej Filip's avatar
Ondřej Filip committed
446 447
    en = ospf_hash_get_header(oa->gr, lsa);
    change = 1;
448 449 450
  }
  else
  {
Ondřej Filip's avatar
Ondřej Filip committed
451 452 453
    if ((en->lsa.length != lsa->length) || (en->lsa.options != lsa->options)
	|| ((en->lsa.age == LSA_MAXAGE) || (lsa->age == LSA_MAXAGE)))
      change = 1;
454 455
    else
    {
Ondřej Filip's avatar
Ondřej Filip committed
456 457
      u8 *k = en->lsa_body, *l = body;
      for (i = 0; i < (lsa->length - sizeof(struct ospf_lsa_header)); i++)
458
      {
Ondřej Filip's avatar
Ondřej Filip committed
459
	if (*(k + i) != *(l + i))
460
	{
Ondřej Filip's avatar
Ondřej Filip committed
461
	  change = 1;
462 463 464
	  break;
	}
      }
465
    }
466
    s_rem_node(SNODE en);
467 468
  }

469
  DBG("Inst lsa: Id: %I, Rt: %I, Type: %u, Age: %u, Sum: %u, Sn: 0x%x\n",
Ondřej Filip's avatar
Ondřej Filip committed
470
      lsa->id, lsa->rt, lsa->type, lsa->age, lsa->checksum, lsa->sn);
471

472
  s_add_tail(&oa->lsal, SNODE en);
Ondřej Filip's avatar
Ondřej Filip committed
473 474 475 476 477 478 479 480
  en->inst_t = now;
  if (en->lsa_body != NULL)
    mb_free(en->lsa_body);
  en->lsa_body = body;
  memcpy(&en->lsa, lsa, sizeof(struct ospf_lsa_header));
  en->ini_age = en->lsa.age;

  if (change)
481
  {
482
    schedule_rtcalc(po);
483
  }
Ondřej Filip's avatar
Ondřej Filip committed
484

485 486
  return en;
}