blob: b8c53fdf74c458117be92ef337d1ddb66f129e43
1 | /*********************************************************************** |
2 | * |
3 | * common.c |
4 | * |
5 | * Implementation of user-space PPPoE redirector for Linux. |
6 | * |
7 | * Common functions used by PPPoE client and server |
8 | * |
9 | * Copyright (C) 2000 by Roaring Penguin Software Inc. |
10 | * |
11 | * This program may be distributed according to the terms of the GNU |
12 | * General Public License, version 2 or (at your option) any later version. |
13 | * |
14 | * LIC: GPL |
15 | * |
16 | ***********************************************************************/ |
17 | |
18 | static char const RCSID[] = |
19 | "$Id$"; |
20 | /* For vsnprintf prototype */ |
21 | #define _ISOC99_SOURCE 1 |
22 | |
23 | /* For seteuid prototype */ |
24 | #define _BSD_SOURCE 1 |
25 | |
26 | #include "pppoe.h" |
27 | |
28 | |
29 | #ifdef HAVE_SYSLOG_H |
30 | #include <syslog.h> |
31 | #include <android/log.h> |
32 | #define syslog(prio, fmt...) \ |
33 | __android_log_print(prio, "PPPOE", fmt) |
34 | #endif |
35 | |
36 | #include <string.h> |
37 | #include <errno.h> |
38 | #include <stdlib.h> |
39 | #include <stdarg.h> |
40 | |
41 | #ifdef HAVE_UNISTD_H |
42 | #include <unistd.h> |
43 | #endif |
44 | |
45 | #include <sys/types.h> |
46 | #include <pwd.h> |
47 | |
48 | /* Are we running SUID or SGID? */ |
49 | int IsSetID = 0; |
50 | |
51 | static uid_t saved_uid = -2; |
52 | static uid_t saved_gid = -2; |
53 | |
54 | /********************************************************************** |
55 | *%FUNCTION: parsePacket |
56 | *%ARGUMENTS: |
57 | * packet -- the PPPoE discovery packet to parse |
58 | * func -- function called for each tag in the packet |
59 | * extra -- an opaque data pointer supplied to parsing function |
60 | *%RETURNS: |
61 | * 0 if everything went well; -1 if there was an error |
62 | *%DESCRIPTION: |
63 | * Parses a PPPoE discovery packet, calling "func" for each tag in the packet. |
64 | * "func" is passed the additional argument "extra". |
65 | ***********************************************************************/ |
66 | int |
67 | parsePacket(PPPoEPacket *packet, ParseFunc *func, void *extra) |
68 | { |
69 | UINT16_t len = ntohs(packet->length); |
70 | unsigned char *curTag; |
71 | UINT16_t tagType, tagLen; |
72 | |
73 | if (packet->ver != 1) { |
74 | syslog(LOG_ERR, "Invalid PPPoE version (%d)", (int) packet->ver); |
75 | return -1; |
76 | } |
77 | if (packet->type != 1) { |
78 | syslog(LOG_ERR, "Invalid PPPoE type (%d)", (int) packet->type); |
79 | return -1; |
80 | } |
81 | |
82 | /* Do some sanity checks on packet */ |
83 | if (len > ETH_DATA_LEN - 6) { /* 6-byte overhead for PPPoE header */ |
84 | syslog(LOG_ERR, "Invalid PPPoE packet length (%u)", len); |
85 | return -1; |
86 | } |
87 | |
88 | /* Step through the tags */ |
89 | curTag = packet->payload; |
90 | while(curTag - packet->payload < len) { |
91 | /* Alignment is not guaranteed, so do this by hand... */ |
92 | tagType = (((UINT16_t) curTag[0]) << 8) + |
93 | (UINT16_t) curTag[1]; |
94 | tagLen = (((UINT16_t) curTag[2]) << 8) + |
95 | (UINT16_t) curTag[3]; |
96 | if (tagType == TAG_END_OF_LIST) { |
97 | return 0; |
98 | } |
99 | if ((curTag - packet->payload) + tagLen + TAG_HDR_SIZE > len) { |
100 | syslog(LOG_ERR, "Invalid PPPoE tag length (%u)", tagLen); |
101 | return -1; |
102 | } |
103 | func(tagType, tagLen, curTag+TAG_HDR_SIZE, extra); |
104 | curTag = curTag + TAG_HDR_SIZE + tagLen; |
105 | } |
106 | return 0; |
107 | } |
108 | |
109 | /********************************************************************** |
110 | *%FUNCTION: findTag |
111 | *%ARGUMENTS: |
112 | * packet -- the PPPoE discovery packet to parse |
113 | * type -- the type of the tag to look for |
114 | * tag -- will be filled in with tag contents |
115 | *%RETURNS: |
116 | * A pointer to the tag if one of the specified type is found; NULL |
117 | * otherwise. |
118 | *%DESCRIPTION: |
119 | * Looks for a specific tag type. |
120 | ***********************************************************************/ |
121 | unsigned char * |
122 | findTag(PPPoEPacket *packet, UINT16_t type, PPPoETag *tag) |
123 | { |
124 | UINT16_t len = ntohs(packet->length); |
125 | unsigned char *curTag; |
126 | UINT16_t tagType, tagLen; |
127 | |
128 | if (packet->ver != 1) { |
129 | syslog(LOG_ERR, "Invalid PPPoE version (%d)", (int) packet->ver); |
130 | return NULL; |
131 | } |
132 | if (packet->type != 1) { |
133 | syslog(LOG_ERR, "Invalid PPPoE type (%d)", (int) packet->type); |
134 | return NULL; |
135 | } |
136 | |
137 | /* Do some sanity checks on packet */ |
138 | if (len > ETH_DATA_LEN - 6) { /* 6-byte overhead for PPPoE header */ |
139 | syslog(LOG_ERR, "Invalid PPPoE packet length (%u)", len); |
140 | return NULL; |
141 | } |
142 | |
143 | /* Step through the tags */ |
144 | curTag = packet->payload; |
145 | while(curTag - packet->payload < len) { |
146 | /* Alignment is not guaranteed, so do this by hand... */ |
147 | tagType = (((UINT16_t) curTag[0]) << 8) + |
148 | (UINT16_t) curTag[1]; |
149 | tagLen = (((UINT16_t) curTag[2]) << 8) + |
150 | (UINT16_t) curTag[3]; |
151 | if (tagType == TAG_END_OF_LIST) { |
152 | return NULL; |
153 | } |
154 | if ((curTag - packet->payload) + tagLen + TAG_HDR_SIZE > len) { |
155 | syslog(LOG_ERR, "Invalid PPPoE tag length (%u)", tagLen); |
156 | return NULL; |
157 | } |
158 | if (tagType == type) { |
159 | memcpy(tag, curTag, tagLen + TAG_HDR_SIZE); |
160 | return curTag; |
161 | } |
162 | curTag = curTag + TAG_HDR_SIZE + tagLen; |
163 | } |
164 | return NULL; |
165 | } |
166 | |
167 | /********************************************************************** |
168 | *%FUNCTION: switchToRealID |
169 | *%ARGUMENTS: |
170 | * None |
171 | *%RETURNS: |
172 | * Nothing |
173 | *%DESCRIPTION: |
174 | * Sets effective user-ID and group-ID to real ones. Aborts on failure |
175 | ***********************************************************************/ |
176 | void |
177 | switchToRealID (void) { |
178 | if (IsSetID) { |
179 | if ((int)saved_uid < 0) saved_uid = geteuid(); |
180 | if ((int)saved_gid < 0) saved_gid = getegid(); |
181 | if (setegid(getgid()) < 0) { |
182 | printErr("setgid failed"); |
183 | exit(EXIT_FAILURE); |
184 | } |
185 | if (seteuid(getuid()) < 0) { |
186 | printErr("seteuid failed"); |
187 | exit(EXIT_FAILURE); |
188 | } |
189 | } |
190 | } |
191 | |
192 | /********************************************************************** |
193 | *%FUNCTION: switchToEffectiveID |
194 | *%ARGUMENTS: |
195 | * None |
196 | *%RETURNS: |
197 | * Nothing |
198 | *%DESCRIPTION: |
199 | * Sets effective user-ID and group-ID back to saved gid/uid |
200 | ***********************************************************************/ |
201 | void |
202 | switchToEffectiveID (void) { |
203 | if (IsSetID) { |
204 | if (setegid(saved_gid) < 0) { |
205 | printErr("setgid failed"); |
206 | exit(EXIT_FAILURE); |
207 | } |
208 | if (seteuid(saved_uid) < 0) { |
209 | printErr("seteuid failed"); |
210 | exit(EXIT_FAILURE); |
211 | } |
212 | } |
213 | } |
214 | |
215 | /********************************************************************** |
216 | *%FUNCTION: dropPrivs |
217 | *%ARGUMENTS: |
218 | * None |
219 | *%RETURNS: |
220 | * Nothing |
221 | *%DESCRIPTION: |
222 | * If effective ID is root, try to become "nobody". If that fails and |
223 | * we're SUID, switch to real user-ID |
224 | ***********************************************************************/ |
225 | void |
226 | dropPrivs(void) |
227 | { |
228 | struct passwd *pw = NULL; |
229 | int ok = 0; |
230 | if (geteuid() == 0) { |
231 | pw = getpwnam("nobody"); |
232 | if (pw) { |
233 | if (setgid(pw->pw_gid) < 0) ok++; |
234 | if (setuid(pw->pw_uid) < 0) ok++; |
235 | } |
236 | } |
237 | if (ok < 2 && IsSetID) { |
238 | setegid(getgid()); |
239 | seteuid(getuid()); |
240 | } |
241 | } |
242 | |
243 | /********************************************************************** |
244 | *%FUNCTION: printErr |
245 | *%ARGUMENTS: |
246 | * str -- error message |
247 | *%RETURNS: |
248 | * Nothing |
249 | *%DESCRIPTION: |
250 | * Prints a message to stderr and syslog. |
251 | ***********************************************************************/ |
252 | void |
253 | printErr(char const *str) |
254 | { |
255 | fprintf(stderr, "pppoe: %s\n", str); |
256 | syslog(LOG_ERR, "%s", str); |
257 | } |
258 | |
259 | |
260 | /********************************************************************** |
261 | *%FUNCTION: strDup |
262 | *%ARGUMENTS: |
263 | * str -- string to copy |
264 | *%RETURNS: |
265 | * A malloc'd copy of str. Exits if malloc fails. |
266 | ***********************************************************************/ |
267 | char * |
268 | strDup(char const *str) |
269 | { |
270 | char *copy = malloc(strlen(str)+1); |
271 | if (!copy) { |
272 | rp_fatal("strdup failed"); |
273 | } |
274 | strcpy(copy, str); |
275 | return copy; |
276 | } |
277 | |
278 | /********************************************************************** |
279 | *%FUNCTION: computeTCPChecksum |
280 | *%ARGUMENTS: |
281 | * ipHdr -- pointer to IP header |
282 | * tcpHdr -- pointer to TCP header |
283 | *%RETURNS: |
284 | * The computed TCP checksum |
285 | ***********************************************************************/ |
286 | UINT16_t |
287 | computeTCPChecksum(unsigned char *ipHdr, unsigned char *tcpHdr) |
288 | { |
289 | UINT32_t sum = 0; |
290 | UINT16_t count = ipHdr[2] * 256 + ipHdr[3]; |
291 | UINT16_t tmp; |
292 | |
293 | unsigned char *addr = tcpHdr; |
294 | unsigned char pseudoHeader[12]; |
295 | |
296 | /* Count number of bytes in TCP header and data */ |
297 | count -= (ipHdr[0] & 0x0F) * 4; |
298 | |
299 | memcpy(pseudoHeader, ipHdr+12, 8); |
300 | pseudoHeader[8] = 0; |
301 | pseudoHeader[9] = ipHdr[9]; |
302 | pseudoHeader[10] = (count >> 8) & 0xFF; |
303 | pseudoHeader[11] = (count & 0xFF); |
304 | |
305 | /* Checksum the pseudo-header */ |
306 | sum += * (UINT16_t *) pseudoHeader; |
307 | sum += * ((UINT16_t *) (pseudoHeader+2)); |
308 | sum += * ((UINT16_t *) (pseudoHeader+4)); |
309 | sum += * ((UINT16_t *) (pseudoHeader+6)); |
310 | sum += * ((UINT16_t *) (pseudoHeader+8)); |
311 | sum += * ((UINT16_t *) (pseudoHeader+10)); |
312 | |
313 | /* Checksum the TCP header and data */ |
314 | while (count > 1) { |
315 | memcpy(&tmp, addr, sizeof(tmp)); |
316 | sum += (UINT32_t) tmp; |
317 | addr += sizeof(tmp); |
318 | count -= sizeof(tmp); |
319 | } |
320 | if (count > 0) { |
321 | sum += (unsigned char) *addr; |
322 | } |
323 | |
324 | while(sum >> 16) { |
325 | sum = (sum & 0xffff) + (sum >> 16); |
326 | } |
327 | return (UINT16_t) ((~sum) & 0xFFFF); |
328 | } |
329 | |
330 | /********************************************************************** |
331 | *%FUNCTION: clampMSS |
332 | *%ARGUMENTS: |
333 | * packet -- PPPoE session packet |
334 | * dir -- either "incoming" or "outgoing" |
335 | * clampMss -- clamp value |
336 | *%RETURNS: |
337 | * Nothing |
338 | *%DESCRIPTION: |
339 | * Clamps MSS option if TCP SYN flag is set. |
340 | ***********************************************************************/ |
341 | void |
342 | clampMSS(PPPoEPacket *packet, char const *dir, int clampMss) |
343 | { |
344 | unsigned char *tcpHdr; |
345 | unsigned char *ipHdr; |
346 | unsigned char *opt; |
347 | unsigned char *endHdr; |
348 | unsigned char *mssopt = NULL; |
349 | UINT16_t csum; |
350 | |
351 | int len, minlen; |
352 | |
353 | /* check PPP protocol type */ |
354 | if (packet->payload[0] & 0x01) { |
355 | /* 8 bit protocol type */ |
356 | |
357 | /* Is it IPv4? */ |
358 | if (packet->payload[0] != 0x21) { |
359 | /* Nope, ignore it */ |
360 | return; |
361 | } |
362 | |
363 | ipHdr = packet->payload + 1; |
364 | minlen = 41; |
365 | } else { |
366 | /* 16 bit protocol type */ |
367 | |
368 | /* Is it IPv4? */ |
369 | if (packet->payload[0] != 0x00 || |
370 | packet->payload[1] != 0x21) { |
371 | /* Nope, ignore it */ |
372 | return; |
373 | } |
374 | |
375 | ipHdr = packet->payload + 2; |
376 | minlen = 42; |
377 | } |
378 | |
379 | /* Is it too short? */ |
380 | len = (int) ntohs(packet->length); |
381 | if (len < minlen) { |
382 | /* 20 byte IP header; 20 byte TCP header; at least 1 or 2 byte PPP protocol */ |
383 | return; |
384 | } |
385 | |
386 | /* Verify once more that it's IPv4 */ |
387 | if ((ipHdr[0] & 0xF0) != 0x40) { |
388 | return; |
389 | } |
390 | |
391 | /* Is it a fragment that's not at the beginning of the packet? */ |
392 | if ((ipHdr[6] & 0x1F) || ipHdr[7]) { |
393 | /* Yup, don't touch! */ |
394 | return; |
395 | } |
396 | /* Is it TCP? */ |
397 | if (ipHdr[9] != 0x06) { |
398 | return; |
399 | } |
400 | |
401 | /* Get start of TCP header */ |
402 | tcpHdr = ipHdr + (ipHdr[0] & 0x0F) * 4; |
403 | |
404 | /* Is SYN set? */ |
405 | if (!(tcpHdr[13] & 0x02)) { |
406 | return; |
407 | } |
408 | |
409 | /* Compute and verify TCP checksum -- do not touch a packet with a bad |
410 | checksum */ |
411 | csum = computeTCPChecksum(ipHdr, tcpHdr); |
412 | if (csum) { |
413 | syslog(LOG_ERR, "Bad TCP checksum %x", (unsigned int) csum); |
414 | |
415 | /* Upper layers will drop it */ |
416 | return; |
417 | } |
418 | |
419 | /* Look for existing MSS option */ |
420 | endHdr = tcpHdr + ((tcpHdr[12] & 0xF0) >> 2); |
421 | opt = tcpHdr + 20; |
422 | while (opt < endHdr) { |
423 | if (!*opt) break; /* End of options */ |
424 | switch(*opt) { |
425 | case 1: |
426 | opt++; |
427 | break; |
428 | |
429 | case 2: |
430 | if (opt[1] != 4) { |
431 | /* Something fishy about MSS option length. */ |
432 | syslog(LOG_ERR, |
433 | "Bogus length for MSS option (%u) from %u.%u.%u.%u", |
434 | (unsigned int) opt[1], |
435 | (unsigned int) ipHdr[12], |
436 | (unsigned int) ipHdr[13], |
437 | (unsigned int) ipHdr[14], |
438 | (unsigned int) ipHdr[15]); |
439 | return; |
440 | } |
441 | mssopt = opt; |
442 | break; |
443 | default: |
444 | if (opt[1] < 2) { |
445 | /* Someone's trying to attack us? */ |
446 | syslog(LOG_ERR, |
447 | "Bogus TCP option length (%u) from %u.%u.%u.%u", |
448 | (unsigned int) opt[1], |
449 | (unsigned int) ipHdr[12], |
450 | (unsigned int) ipHdr[13], |
451 | (unsigned int) ipHdr[14], |
452 | (unsigned int) ipHdr[15]); |
453 | return; |
454 | } |
455 | opt += (opt[1]); |
456 | break; |
457 | } |
458 | /* Found existing MSS option? */ |
459 | if (mssopt) break; |
460 | } |
461 | |
462 | /* If MSS exists and it's low enough, do nothing */ |
463 | if (mssopt) { |
464 | unsigned mss = mssopt[2] * 256 + mssopt[3]; |
465 | if (mss <= (unsigned int)clampMss) { |
466 | return; |
467 | } |
468 | |
469 | mssopt[2] = (((unsigned) clampMss) >> 8) & 0xFF; |
470 | mssopt[3] = ((unsigned) clampMss) & 0xFF; |
471 | } else { |
472 | /* No MSS option. Don't add one; we'll have to use 536. */ |
473 | return; |
474 | } |
475 | |
476 | /* Recompute TCP checksum */ |
477 | tcpHdr[16] = 0; |
478 | tcpHdr[17] = 0; |
479 | csum = computeTCPChecksum(ipHdr, tcpHdr); |
480 | (* (UINT16_t *) (tcpHdr+16)) = csum; |
481 | } |
482 | |
483 | /*********************************************************************** |
484 | *%FUNCTION: sendPADT |
485 | *%ARGUMENTS: |
486 | * conn -- PPPoE connection |
487 | * msg -- if non-NULL, extra error message to include in PADT packet. |
488 | *%RETURNS: |
489 | * Nothing |
490 | *%DESCRIPTION: |
491 | * Sends a PADT packet |
492 | ***********************************************************************/ |
493 | void |
494 | sendPADT(PPPoEConnection *conn, char const *msg) |
495 | { |
496 | PPPoEPacket packet; |
497 | unsigned char *cursor = packet.payload; |
498 | |
499 | UINT16_t plen = 0; |
500 | |
501 | /* Do nothing if no session established yet */ |
502 | if (!conn->session) return; |
503 | |
504 | /* Do nothing if no discovery socket */ |
505 | if (conn->discoverySocket < 0) return; |
506 | |
507 | memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN); |
508 | memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN); |
509 | |
510 | packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); |
511 | packet.ver = 1; |
512 | packet.type = 1; |
513 | packet.code = CODE_PADT; |
514 | packet.session = conn->session; |
515 | |
516 | /* Reset Session to zero so there is no possibility of |
517 | recursive calls to this function by any signal handler */ |
518 | conn->session = 0; |
519 | |
520 | /* If we're using Host-Uniq, copy it over */ |
521 | if (conn->useHostUniq) { |
522 | PPPoETag hostUniq; |
523 | pid_t pid = getpid(); |
524 | hostUniq.type = htons(TAG_HOST_UNIQ); |
525 | hostUniq.length = htons(sizeof(pid)); |
526 | memcpy(hostUniq.payload, &pid, sizeof(pid)); |
527 | memcpy(cursor, &hostUniq, sizeof(pid) + TAG_HDR_SIZE); |
528 | cursor += sizeof(pid) + TAG_HDR_SIZE; |
529 | plen += sizeof(pid) + TAG_HDR_SIZE; |
530 | } |
531 | |
532 | /* Copy error message */ |
533 | if (msg) { |
534 | PPPoETag err; |
535 | size_t elen = strlen(msg); |
536 | err.type = htons(TAG_GENERIC_ERROR); |
537 | err.length = htons(elen); |
538 | strcpy((char *) err.payload, msg); |
539 | memcpy(cursor, &err, elen + TAG_HDR_SIZE); |
540 | cursor += elen + TAG_HDR_SIZE; |
541 | plen += elen + TAG_HDR_SIZE; |
542 | } |
543 | |
544 | /* Copy cookie and relay-ID if needed */ |
545 | if (conn->cookie.type) { |
546 | CHECK_ROOM(cursor, packet.payload, |
547 | ntohs(conn->cookie.length) + TAG_HDR_SIZE); |
548 | memcpy(cursor, &conn->cookie, ntohs(conn->cookie.length) + TAG_HDR_SIZE); |
549 | cursor += ntohs(conn->cookie.length) + TAG_HDR_SIZE; |
550 | plen += ntohs(conn->cookie.length) + TAG_HDR_SIZE; |
551 | } |
552 | |
553 | if (conn->relayId.type) { |
554 | CHECK_ROOM(cursor, packet.payload, |
555 | ntohs(conn->relayId.length) + TAG_HDR_SIZE); |
556 | memcpy(cursor, &conn->relayId, ntohs(conn->relayId.length) + TAG_HDR_SIZE); |
557 | cursor += ntohs(conn->relayId.length) + TAG_HDR_SIZE; |
558 | plen += ntohs(conn->relayId.length) + TAG_HDR_SIZE; |
559 | } |
560 | |
561 | packet.length = htons(plen); |
562 | sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE)); |
563 | #ifdef DEBUGGING_ENABLED |
564 | if (conn->debugFile) { |
565 | dumpPacket(conn->debugFile, &packet, "SENT"); |
566 | fprintf(conn->debugFile, "\n"); |
567 | fflush(conn->debugFile); |
568 | } |
569 | #endif |
570 | syslog(LOG_INFO,"Sent PADT"); |
571 | } |
572 | |
573 | /*********************************************************************** |
574 | *%FUNCTION: sendPADTf |
575 | *%ARGUMENTS: |
576 | * conn -- PPPoE connection |
577 | * msg -- printf-style format string |
578 | * args -- arguments for msg |
579 | *%RETURNS: |
580 | * Nothing |
581 | *%DESCRIPTION: |
582 | * Sends a PADT packet with a formatted message |
583 | ***********************************************************************/ |
584 | void |
585 | sendPADTf(PPPoEConnection *conn, char const *fmt, ...) |
586 | { |
587 | char msg[512]; |
588 | va_list ap; |
589 | |
590 | va_start(ap, fmt); |
591 | vsnprintf(msg, sizeof(msg), fmt, ap); |
592 | va_end(ap); |
593 | msg[511] = 0; |
594 | |
595 | sendPADT(conn, msg); |
596 | } |
597 | |
598 | /********************************************************************** |
599 | *%FUNCTION: pktLogErrs |
600 | *%ARGUMENTS: |
601 | * pkt -- packet type (a string) |
602 | * type -- tag type |
603 | * len -- tag length |
604 | * data -- tag data |
605 | * extra -- extra user data |
606 | *%RETURNS: |
607 | * Nothing |
608 | *%DESCRIPTION: |
609 | * Logs error tags |
610 | ***********************************************************************/ |
611 | void |
612 | pktLogErrs(char const *pkt, |
613 | UINT16_t type, UINT16_t len, unsigned char *data, |
614 | void *extra) |
615 | { |
616 | char const *str; |
617 | char const *fmt = "%s: %s: %.*s"; |
618 | switch(type) { |
619 | case TAG_SERVICE_NAME_ERROR: |
620 | str = "Service-Name-Error"; |
621 | break; |
622 | case TAG_AC_SYSTEM_ERROR: |
623 | str = "System-Error"; |
624 | break; |
625 | default: |
626 | str = "Generic-Error"; |
627 | } |
628 | |
629 | syslog(LOG_ERR, fmt, pkt, str, (int) len, data); |
630 | fprintf(stderr, fmt, pkt, str, (int) len, data); |
631 | fprintf(stderr, "\n"); |
632 | } |
633 | |
634 | /********************************************************************** |
635 | *%FUNCTION: parseLogErrs |
636 | *%ARGUMENTS: |
637 | * type -- tag type |
638 | * len -- tag length |
639 | * data -- tag data |
640 | * extra -- extra user data |
641 | *%RETURNS: |
642 | * Nothing |
643 | *%DESCRIPTION: |
644 | * Picks error tags out of a packet and logs them. |
645 | ***********************************************************************/ |
646 | void |
647 | parseLogErrs(UINT16_t type, UINT16_t len, unsigned char *data, |
648 | void *extra) |
649 | { |
650 | pktLogErrs("PADT", type, len, data, extra); |
651 | } |
652 |