]>
Commit | Line | Data |
---|---|---|
4364b47e OF |
1 | /* |
2 | * BIRD -- OSPF | |
3 | * | |
e6ea2e37 | 4 | * (c) 1999--2005 Ondrej Filip <feela@network.cz> |
4364b47e OF |
5 | * |
6 | * Can be freely distributed and used under the terms of the GNU GPL. | |
7 | */ | |
8 | ||
9 | #include "ospf.h" | |
3e2bd0f1 OF |
10 | #include "nest/password.h" |
11 | #include "lib/md5.h" | |
4364b47e OF |
12 | |
13 | void | |
3e2bd0f1 | 14 | ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type) |
4364b47e OF |
15 | { |
16 | struct ospf_packet *pkt; | |
86c84d76 | 17 | struct proto *p = (struct proto *) (ifa->oa->po); |
4364b47e | 18 | |
00bd27a1 | 19 | pkt = (struct ospf_packet *) buf; |
4364b47e | 20 | |
00bd27a1 | 21 | pkt->version = OSPF_VERSION; |
4364b47e | 22 | |
00bd27a1 OF |
23 | pkt->type = h_type; |
24 | ||
2e10a170 | 25 | pkt->routerid = htonl(p->cf->global->router_id); |
3b16080c | 26 | pkt->areaid = htonl(ifa->oa->areaid); |
c3226991 OZ |
27 | |
28 | #ifdef OSPFv3 | |
29 | pkt->instance_id = ifa->instance_id; | |
30 | #endif | |
31 | ||
32 | #ifdef OSPFv2 | |
2e10a170 | 33 | pkt->autype = htons(ifa->autype); |
c3226991 OZ |
34 | #endif |
35 | ||
00bd27a1 | 36 | pkt->checksum = 0; |
4364b47e OF |
37 | } |
38 | ||
3e2bd0f1 OF |
39 | unsigned |
40 | ospf_pkt_maxsize(struct ospf_iface *ifa) | |
41 | { | |
c3226991 | 42 | /* For virtual links use mtu=576, can be mtu < 576? */ |
e6ea2e37 | 43 | unsigned mtu = (ifa->type == OSPF_IT_VLINK) ? OSPF_VLINK_MTU : ifa->iface->mtu; |
c3226991 OZ |
44 | unsigned add = 0; |
45 | ||
46 | #ifdef OSPFv2 | |
47 | add = ((ifa->autype == OSPF_AUTH_CRYPT) ? OSPF_AUTH_CRYPT_SIZE : 0); | |
48 | #endif | |
49 | ||
e6ea2e37 | 50 | return ((mtu <= ifa->iface->mtu) ? mtu : ifa->iface->mtu) - |
c3226991 | 51 | SIZE_OF_IP_HEADER - add; |
3e2bd0f1 OF |
52 | } |
53 | ||
c3226991 OZ |
54 | |
55 | #ifdef OSPFv2 | |
56 | ||
57 | static void | |
3e2bd0f1 | 58 | ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt) |
4364b47e | 59 | { |
b21f68b4 | 60 | struct password_item *passwd = NULL; |
3e2bd0f1 OF |
61 | void *tail; |
62 | struct MD5Context ctxt; | |
63 | char password[OSPF_AUTH_CRYPT_SIZE]; | |
64 | ||
2e10a170 | 65 | pkt->autype = htons(ifa->autype); |
3e2bd0f1 OF |
66 | |
67 | switch(ifa->autype) | |
68 | { | |
3e2bd0f1 | 69 | case OSPF_AUTH_SIMPLE: |
32d3228d | 70 | bzero(&pkt->u, sizeof(union ospf_auth)); |
b21f68b4 | 71 | passwd = password_find(ifa->passwords, 1); |
32d3228d OF |
72 | if (!passwd) |
73 | { | |
74 | log( L_ERR "No suitable password found for authentication" ); | |
75 | return; | |
76 | } | |
77 | password_cpy(pkt->u.password, passwd->password, sizeof(union ospf_auth)); | |
69b27ed6 | 78 | case OSPF_AUTH_NONE: |
32d3228d OF |
79 | pkt->checksum = ipsum_calculate(pkt, sizeof(struct ospf_packet) - |
80 | sizeof(union ospf_auth), (pkt + 1), | |
3e2bd0f1 OF |
81 | ntohs(pkt->length) - |
82 | sizeof(struct ospf_packet), NULL); | |
3e2bd0f1 OF |
83 | break; |
84 | case OSPF_AUTH_CRYPT: | |
b21f68b4 | 85 | passwd = password_find(ifa->passwords, 0); |
3e2bd0f1 OF |
86 | if (!passwd) |
87 | { | |
88 | log( L_ERR "No suitable password found for authentication" ); | |
89 | return; | |
90 | } | |
91 | ||
92 | pkt->checksum = 0; | |
93 | ||
024c310b OZ |
94 | /* Perhaps use random value to prevent replay attacks after |
95 | reboot when system does not have independent RTC? */ | |
3e2bd0f1 | 96 | if (!ifa->csn) |
024c310b OZ |
97 | { |
98 | ifa->csn = (u32) now; | |
99 | ifa->csn_use = now; | |
100 | } | |
101 | ||
102 | /* We must have sufficient delay between sending a packet and increasing | |
103 | CSN to prevent reordering of packets (in a network) with different CSNs */ | |
104 | if ((now - ifa->csn_use) > 1) | |
105 | ifa->csn++; | |
106 | ||
107 | ifa->csn_use = now; | |
3e2bd0f1 OF |
108 | |
109 | pkt->u.md5.keyid = passwd->id; | |
110 | pkt->u.md5.len = OSPF_AUTH_CRYPT_SIZE; | |
111 | pkt->u.md5.zero = 0; | |
024c310b | 112 | pkt->u.md5.csn = htonl(ifa->csn); |
3e2bd0f1 OF |
113 | tail = ((void *)pkt) + ntohs(pkt->length); |
114 | MD5Init(&ctxt); | |
115 | MD5Update(&ctxt, (char *) pkt, ntohs(pkt->length)); | |
116 | password_cpy(password, passwd->password, OSPF_AUTH_CRYPT_SIZE); | |
117 | MD5Update(&ctxt, password, OSPF_AUTH_CRYPT_SIZE); | |
118 | MD5Final(tail, &ctxt); | |
3e2bd0f1 OF |
119 | break; |
120 | default: | |
121 | bug("Unknown authentication type"); | |
122 | } | |
4364b47e OF |
123 | } |
124 | ||
9831e591 | 125 | static int |
69b27ed6 | 126 | ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_packet *pkt, int size) |
c1824c4d | 127 | { |
86c84d76 | 128 | struct proto_ospf *po = ifa->oa->po; |
3e2bd0f1 OF |
129 | struct proto *p = &po->proto; |
130 | struct password_item *pass = NULL, *ptmp; | |
131 | void *tail; | |
132 | char md5sum[OSPF_AUTH_CRYPT_SIZE]; | |
133 | char password[OSPF_AUTH_CRYPT_SIZE]; | |
134 | struct MD5Context ctxt; | |
135 | ||
136 | ||
2e10a170 | 137 | if (pkt->autype != htons(ifa->autype)) |
2e10a170 | 138 | { |
3e2bd0f1 OF |
139 | OSPF_TRACE(D_PACKETS, "OSPF_auth: Method differs (%d)", ntohs(pkt->autype)); |
140 | return 0; | |
2e10a170 | 141 | } |
c1824c4d | 142 | |
69b27ed6 OF |
143 | if (n && (ifa != n->ifa)) |
144 | { | |
145 | OSPF_TRACE(D_PACKETS, "OSPF_auth: received packet from strange interface (%s/%s)", | |
146 | ifa->iface->name, n->ifa->iface->name); | |
147 | return 0; | |
148 | } | |
149 | ||
3e2bd0f1 OF |
150 | switch(ifa->autype) |
151 | { | |
152 | case OSPF_AUTH_NONE: | |
153 | return 1; | |
154 | break; | |
155 | case OSPF_AUTH_SIMPLE: | |
b21f68b4 | 156 | pass = password_find(ifa->passwords, 1); |
3b108f18 | 157 | if (!pass) |
3e2bd0f1 OF |
158 | { |
159 | OSPF_TRACE(D_PACKETS, "OSPF_auth: no password found"); | |
160 | return 0; | |
161 | } | |
32d3228d | 162 | password_cpy(password, pass->password, sizeof(union ospf_auth)); |
4364b47e | 163 | |
32d3228d | 164 | if (memcmp(pkt->u.password, password, sizeof(union ospf_auth))) |
3e2bd0f1 | 165 | { |
32d3228d OF |
166 | char ppass[sizeof(union ospf_auth) + 1]; |
167 | bzero(ppass, (sizeof(union ospf_auth) + 1)); | |
168 | memcpy(ppass, pkt->u.password, sizeof(union ospf_auth)); | |
169 | OSPF_TRACE(D_PACKETS, "OSPF_auth: different passwords (%s)", ppass); | |
3e2bd0f1 OF |
170 | return 0; |
171 | } | |
32d3228d | 172 | return 1; |
3e2bd0f1 OF |
173 | break; |
174 | case OSPF_AUTH_CRYPT: | |
175 | if (pkt->u.md5.len != OSPF_AUTH_CRYPT_SIZE) | |
176 | { | |
177 | OSPF_TRACE(D_PACKETS, "OSPF_auth: wrong size of md5 digest"); | |
178 | return 0; | |
179 | } | |
3b108f18 | 180 | |
3e2bd0f1 OF |
181 | if (ntohs(pkt->length) + OSPF_AUTH_CRYPT_SIZE != size) |
182 | { | |
f39e3bfd | 183 | OSPF_TRACE(D_PACKETS, "OSPF_auth: size mismatch (%d vs %d)", |
5d3f5552 | 184 | ntohs(pkt->length) + OSPF_AUTH_CRYPT_SIZE, size); |
3e2bd0f1 OF |
185 | return 0; |
186 | } | |
187 | ||
188 | if (pkt->u.md5.zero) | |
189 | { | |
190 | OSPF_TRACE(D_PACKETS, "OSPF_auth: \"zero\" area is non-zero"); | |
191 | return 0; | |
192 | } | |
193 | ||
194 | tail = ((void *)pkt) + ntohs(pkt->length); | |
195 | ||
3b108f18 | 196 | if (ifa->passwords) |
3e2bd0f1 | 197 | { |
3b108f18 OZ |
198 | WALK_LIST(ptmp, *(ifa->passwords)) |
199 | { | |
200 | if (pkt->u.md5.keyid != ptmp->id) continue; | |
201 | if ((ptmp->accfrom > now_real) || (ptmp->accto < now_real)) continue; | |
202 | pass = ptmp; | |
203 | break; | |
204 | } | |
3e2bd0f1 OF |
205 | } |
206 | ||
3b108f18 | 207 | if (!pass) |
3e2bd0f1 OF |
208 | { |
209 | OSPF_TRACE(D_PACKETS, "OSPF_auth: no suitable md5 password found"); | |
210 | return 0; | |
211 | } | |
212 | ||
3b108f18 | 213 | if (n) |
3e2bd0f1 | 214 | { |
024c310b OZ |
215 | u32 rcv_csn = ntohl(pkt->u.md5.csn); |
216 | if(rcv_csn < n->csn) | |
217 | { | |
218 | OSPF_TRACE(D_PACKETS, "OSPF_auth: lower sequence number (rcv %d, old %d)", rcv_csn, n->csn); | |
219 | return 0; | |
220 | } | |
221 | ||
222 | n->csn = rcv_csn; | |
3e2bd0f1 OF |
223 | } |
224 | ||
225 | MD5Init(&ctxt); | |
226 | MD5Update(&ctxt, (char *) pkt, ntohs(pkt->length)); | |
227 | password_cpy(password, pass->password, OSPF_AUTH_CRYPT_SIZE); | |
228 | MD5Update(&ctxt, password, OSPF_AUTH_CRYPT_SIZE); | |
229 | MD5Final(md5sum, &ctxt); | |
bc956fca | 230 | if (memcmp(md5sum, tail, OSPF_AUTH_CRYPT_SIZE)) |
3e2bd0f1 OF |
231 | { |
232 | OSPF_TRACE(D_PACKETS, "OSPF_auth: wrong md5 digest"); | |
233 | return 0; | |
234 | } | |
235 | return 1; | |
236 | break; | |
237 | default: | |
238 | OSPF_TRACE(D_PACKETS, "OSPF_auth: unknown auth type"); | |
239 | return 0; | |
240 | } | |
4364b47e OF |
241 | } |
242 | ||
c3226991 OZ |
243 | #else |
244 | ||
245 | /* OSPFv3 authentication not yet supported */ | |
246 | ||
247 | static inline void | |
248 | ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt) | |
249 | { } | |
250 | ||
251 | static int | |
252 | ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_packet *pkt, int size) | |
253 | { return 1; } | |
254 | ||
255 | #endif | |
256 | ||
351feeb5 OF |
257 | /** |
258 | * ospf_rx_hook | |
baa5dd6c | 259 | * @sk: socket we received the packet. Its ignored. |
351feeb5 OF |
260 | * @size: size of the packet |
261 | * | |
baa5dd6c OF |
262 | * This is the entry point for messages from neighbors. Many checks (like |
263 | * authentication, checksums, size) are done before the packet is passed to | |
351feeb5 OF |
264 | * non generic functions. |
265 | */ | |
4364b47e | 266 | int |
2e10a170 | 267 | ospf_rx_hook(sock * sk, int size) |
4364b47e | 268 | { |
4364b47e | 269 | struct ospf_packet *ps; |
00bd27a1 | 270 | struct ospf_iface *ifa = (struct ospf_iface *) (sk->data); |
86c84d76 OF |
271 | struct proto_ospf *po = ifa->oa->po; |
272 | struct proto *p = &po->proto; | |
5e3436d2 | 273 | struct ospf_neighbor *n; |
b4d8a0c2 | 274 | int osize; |
5e3436d2 | 275 | char *mesg = "Bad OSPF packet from "; |
3b16080c | 276 | struct ospf_iface *iff; |
4364b47e | 277 | |
00bd27a1 OF |
278 | if (ifa->stub) |
279 | return (1); | |
280 | ||
b49e6f5a | 281 | ps = (struct ospf_packet *) ip_skip_header(sk->rbuf, &size); |
3b16080c | 282 | |
0e9617e4 OZ |
283 | if (ps == NULL) |
284 | { | |
285 | log(L_ERR "%s%I - bad IP header", mesg, sk->faddr); | |
286 | return 1; | |
287 | } | |
288 | ||
3b16080c OF |
289 | if ((ifa->oa->areaid != 0) && (ntohl(ps->areaid) == 0)) |
290 | { | |
291 | WALK_LIST(iff, po->iface_list) | |
292 | { | |
293 | if ((iff->type == OSPF_IT_VLINK) && (iff->iface == ifa->iface) && | |
294 | (iff->voa = ifa->oa) && ipa_equal(sk->faddr, iff->vip)) | |
295 | { | |
296 | return 1; /* Packet is for VLINK */ | |
297 | } | |
298 | } | |
299 | } | |
300 | ||
2e10a170 | 301 | DBG("%s: RX_Hook called on interface %s.\n", p->name, sk->iface->name); |
00bd27a1 | 302 | |
b4d8a0c2 | 303 | osize = ntohs(ps->length); |
00bd27a1 | 304 | |
2e10a170 OF |
305 | if ((unsigned) size < sizeof(struct ospf_packet)) |
306 | { | |
5e3436d2 OF |
307 | log(L_ERR "%s%I - too short (%u bytes)", mesg, sk->faddr, size); |
308 | return 1; | |
2e10a170 | 309 | } |
00bd27a1 | 310 | |
b4d8a0c2 | 311 | if ((osize > size) || (osize != (4 * (osize / 4)))) |
2e10a170 | 312 | { |
b4d8a0c2 | 313 | log(L_ERR "%s%I - size field does not match (%d/%d)", mesg, sk->faddr, ntohs(ps->length), size ); |
5e3436d2 | 314 | return 1; |
2e10a170 | 315 | } |
00bd27a1 OF |
316 | |
317 | if (ps->version != OSPF_VERSION) | |
2e10a170 | 318 | { |
5e3436d2 OF |
319 | log(L_ERR "%s%I - version %u", mesg, sk->faddr, ps->version); |
320 | return 1; | |
2e10a170 | 321 | } |
00bd27a1 | 322 | |
c3226991 OZ |
323 | /* FIXME - handle checksums in OSPFv3 */ |
324 | #ifdef OSPFv2 | |
bc956fca OF |
325 | if ((ps->autype != htons(OSPF_AUTH_CRYPT)) && |
326 | (!ipsum_verify(ps, 16, (void *) ps + sizeof(struct ospf_packet), | |
3e2bd0f1 | 327 | ntohs(ps->length) - sizeof(struct ospf_packet), NULL))) |
2e10a170 | 328 | { |
5e3436d2 OF |
329 | log(L_ERR "%s%I - bad checksum", mesg, sk->faddr); |
330 | return 1; | |
2e10a170 | 331 | } |
c3226991 | 332 | #endif |
00bd27a1 | 333 | |
3b16080c | 334 | if (ntohl(ps->areaid) != ifa->oa->areaid) |
2e10a170 | 335 | { |
c3226991 | 336 | log(L_ERR "%s%I - different area (%u)", mesg, sk->faddr, ntohl(ps->areaid)); |
5e3436d2 | 337 | return 1; |
2e10a170 | 338 | } |
00bd27a1 | 339 | |
c3226991 OZ |
340 | /* FIXME - handling of instance id should be better */ |
341 | #ifdef OSPFv3 | |
342 | if (ps->instance_id != ifa->instance_id) | |
343 | { | |
344 | log(L_ERR "%s%I - different instance (%u)", mesg, sk->faddr, ps->instance_id); | |
345 | return 1; | |
346 | } | |
347 | #endif | |
348 | ||
2e10a170 OF |
349 | if (ntohl(ps->routerid) == p->cf->global->router_id) |
350 | { | |
5e3436d2 OF |
351 | log(L_ERR "%s%I - received my own router ID!", mesg, sk->faddr); |
352 | return 1; | |
2e10a170 | 353 | } |
00bd27a1 | 354 | |
2e10a170 OF |
355 | if (ntohl(ps->routerid) == 0) |
356 | { | |
5e3436d2 OF |
357 | log(L_ERR "%s%I - router id = 0.0.0.0", mesg, sk->faddr); |
358 | return 1; | |
359 | } | |
360 | ||
427e5993 | 361 | if (((unsigned) size > sk->rbsize) || (ntohs(ps->length) > sk->rbsize)) |
5e3436d2 | 362 | { |
427e5993 OF |
363 | log(L_ERR "%s%I - packet is too large (%d-%d vs %d)", |
364 | mesg, sk->faddr, size, ntohs(ps->length), sk->rbsize); | |
5e3436d2 OF |
365 | return 1; |
366 | } | |
367 | ||
8cc598a5 OZ |
368 | /* This is deviation from RFC 2328 - neighbours should be identified by |
369 | * IP address on broadcast and NBMA networks. | |
370 | */ | |
5e3436d2 OF |
371 | n = find_neigh(ifa, ntohl(((struct ospf_packet *) ps)->routerid)); |
372 | ||
373 | if(!n && (ps->type != HELLO_P)) | |
374 | { | |
375 | OSPF_TRACE(D_PACKETS, "Received non-hello packet from uknown neighbor (%I)", sk->faddr); | |
376 | return 1; | |
2e10a170 | 377 | } |
00bd27a1 | 378 | |
69b27ed6 | 379 | if (!ospf_pkt_checkauth(n, ifa, ps, size)) |
3e2bd0f1 OF |
380 | { |
381 | log(L_ERR "%s%I - authentification failed", mesg, sk->faddr); | |
382 | return 1; | |
383 | } | |
384 | ||
351feeb5 | 385 | /* Dump packet |
00bd27a1 OF |
386 | pu8=(u8 *)(sk->rbuf+5*4); |
387 | for(i=0;i<ntohs(ps->length);i+=4) | |
388 | DBG("%s: received %u,%u,%u,%u\n",p->name, pu8[i+0], pu8[i+1], pu8[i+2], | |
389 | pu8[i+3]); | |
390 | DBG("%s: received size: %u\n",p->name,size); | |
391 | */ | |
392 | ||
393 | switch (ps->type) | |
2e10a170 OF |
394 | { |
395 | case HELLO_P: | |
396 | DBG("%s: Hello received.\n", p->name); | |
c3226991 | 397 | ospf_hello_receive(ps, ifa, n, sk->faddr); |
2e10a170 OF |
398 | break; |
399 | case DBDES_P: | |
400 | DBG("%s: Database description received.\n", p->name); | |
c3226991 | 401 | ospf_dbdes_receive(ps, ifa, n); |
2e10a170 OF |
402 | break; |
403 | case LSREQ_P: | |
404 | DBG("%s: Link state request received.\n", p->name); | |
c3226991 | 405 | ospf_lsreq_receive(ps, ifa, n); |
2e10a170 OF |
406 | break; |
407 | case LSUPD_P: | |
408 | DBG("%s: Link state update received.\n", p->name); | |
c3226991 | 409 | ospf_lsupd_receive(ps, ifa, n); |
2e10a170 OF |
410 | break; |
411 | case LSACK_P: | |
412 | DBG("%s: Link state ack received.\n", p->name); | |
c3226991 | 413 | ospf_lsack_receive(ps, ifa, n); |
2e10a170 OF |
414 | break; |
415 | default: | |
5e3436d2 OF |
416 | log(L_ERR "%s%I - wrong type %u", mesg, sk->faddr, ps->type); |
417 | return 1; | |
2e10a170 | 418 | }; |
5e3436d2 | 419 | return 1; |
4364b47e OF |
420 | } |
421 | ||
422 | void | |
2e10a170 | 423 | ospf_tx_hook(sock * sk) |
4364b47e | 424 | { |
86c84d76 OF |
425 | struct ospf_iface *ifa= (struct ospf_iface *) (sk->data); |
426 | struct proto *p = (struct proto *) (ifa->oa->po); | |
5d3f5552 | 427 | log(L_ERR "%s: TX_Hook called on interface %s\n", p->name, sk->iface->name); |
4364b47e OF |
428 | } |
429 | ||
430 | void | |
2eef9e88 | 431 | ospf_err_hook(sock * sk, int err) |
4364b47e | 432 | { |
86c84d76 OF |
433 | struct ospf_iface *ifa= (struct ospf_iface *) (sk->data); |
434 | struct proto *p = (struct proto *) (ifa->oa->po); | |
2eef9e88 OF |
435 | log(L_ERR "%s: Err_Hook called on interface %s with err=%d\n", |
436 | p->name, sk->iface->name, err); | |
4364b47e OF |
437 | } |
438 | ||
67315ef6 | 439 | void |
f9c799a0 | 440 | ospf_send_to_agt(struct ospf_iface *ifa, u8 state) |
67315ef6 OF |
441 | { |
442 | struct ospf_neighbor *n; | |
443 | ||
b181f444 | 444 | WALK_LIST(n, ifa->neigh_list) if (n->state >= state) |
f9c799a0 | 445 | ospf_send_to(ifa, n->ip); |
00bd27a1 | 446 | } |
eb436e16 OF |
447 | |
448 | void | |
f9c799a0 | 449 | ospf_send_to_bdr(struct ospf_iface *ifa) |
eb436e16 | 450 | { |
3b16080c | 451 | if (!ipa_equal(ifa->drip, IPA_NONE)) |
f9c799a0 | 452 | ospf_send_to(ifa, ifa->drip); |
3b16080c | 453 | if (!ipa_equal(ifa->bdrip, IPA_NONE)) |
f9c799a0 | 454 | ospf_send_to(ifa, ifa->bdrip); |
eb436e16 | 455 | } |
98ac6176 OF |
456 | |
457 | void | |
f9c799a0 | 458 | ospf_send_to(struct ospf_iface *ifa, ip_addr ip) |
98ac6176 | 459 | { |
f9c799a0 | 460 | sock *sk = ifa->sk; |
3e2bd0f1 | 461 | struct ospf_packet *pkt = (struct ospf_packet *) sk->tbuf; |
c3226991 | 462 | int len = ntohs(pkt->length); |
3e2bd0f1 | 463 | |
c3226991 OZ |
464 | #ifdef OSPFv2 |
465 | if (ifa->autype == OSPF_AUTH_CRYPT) | |
466 | len += OSPF_AUTH_CRYPT_SIZE; | |
467 | #endif | |
468 | ||
469 | ospf_pkt_finalize(ifa, pkt); | |
f15cb99c OZ |
470 | if (sk->tbuf != sk->tpos) |
471 | log(L_ERR "Aiee, old packet was overwritted in TX buffer"); | |
472 | ||
3e2bd0f1 OF |
473 | if (ipa_equal(ip, IPA_NONE)) |
474 | sk_send(sk, len); | |
475 | else | |
476 | sk_send_to(sk, len, ip, OSPF_PROTO); | |
98ac6176 OF |
477 | } |
478 |