Commit | Line | Data |
---|---|---|
2a905848 RG |
1 | /* |
2 | * ipcp.c - PPP IP Control Protocol. | |
3 | * | |
4 | * Copyright (c) 1989 Carnegie Mellon University. | |
5 | * All rights reserved. | |
6 | * | |
7 | * Redistribution and use in source and binary forms are permitted | |
8 | * provided that the above copyright notice and this paragraph are | |
9 | * duplicated in all such forms and that any documentation, | |
10 | * advertising materials, and other materials related to such | |
11 | * distribution and use acknowledge that the software was developed | |
12 | * by Carnegie Mellon University. The name of the | |
13 | * University may not be used to endorse or promote products derived | |
14 | * from this software without specific prior written permission. | |
15 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | |
16 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | |
17 | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
18 | */ | |
19 | ||
20 | /* | |
21 | * TODO: | |
22 | * Fix IP address negotiation (wantoptions or hisoptions). | |
23 | * Don't set zero IP addresses. | |
24 | * Send NAKs for unsent CIs. | |
25 | * VJ compression. | |
26 | */ | |
27 | ||
28 | #include <stdio.h> | |
29 | #include <syslog.h> | |
30 | #include <sys/ioctl.h> | |
31 | #include <sys/types.h> | |
32 | #include <sys/socket.h> | |
33 | #include <sys/time.h> | |
34 | ||
35 | #include <net/if.h> | |
36 | #include <net/route.h> | |
37 | #include <netinet/in.h> | |
38 | ||
39 | #include <string.h> | |
40 | ||
41 | #ifndef BSD | |
42 | #ifndef sun | |
43 | #define BSD 44 | |
44 | #endif | |
45 | #endif /*BSD*/ | |
46 | ||
47 | #ifdef STREAMS | |
48 | #include <sys/stream.h> | |
49 | #include "ppp_str.h" | |
50 | #endif | |
51 | ||
52 | #include "pppd.h" | |
53 | #include <net/if_ppp.h> | |
54 | ||
55 | #include <net/ppp.h> | |
56 | #include "fsm.h" | |
57 | #include "ipcp.h" | |
58 | ||
59 | ||
60 | /* global vars */ | |
61 | ipcp_options ipcp_wantoptions[NPPP]; /* Options that we want to request */ | |
62 | ipcp_options ipcp_gotoptions[NPPP]; /* Options that peer ack'd */ | |
63 | ipcp_options ipcp_allowoptions[NPPP]; /* Options that we allow peer to | |
64 | request */ | |
65 | ipcp_options ipcp_hisoptions[NPPP]; /* Options that we ack'd */ | |
66 | ||
67 | /* local vars */ | |
68 | ||
69 | /* | |
70 | * VJ compression protocol mode for negotiation. See ipcp.h for a | |
71 | * description of each mode. | |
72 | */ | |
73 | static int vj_mode = IPCP_VJMODE_RFC1332; | |
74 | ||
75 | static int vj_opt_len = 6; /* holds length in octets for valid vj */ | |
76 | /* compression frame depending on mode */ | |
77 | ||
78 | static int vj_opt_val = IPCP_VJ_COMP; | |
79 | /* compression negotiation frames */ | |
80 | /* depending on vj_mode */ | |
81 | ||
82 | static void ipcp_resetci __ARGS((fsm *)); /* Reset our Configuration Information */ | |
83 | static int ipcp_cilen __ARGS((fsm *)); /* Return length of our CI */ | |
84 | static void ipcp_addci __ARGS((fsm *, u_char *)); /* Add our CIs */ | |
85 | static int ipcp_ackci __ARGS((fsm *, u_char *, int)); /* Ack some CIs */ | |
86 | static void ipcp_nakci __ARGS((fsm *, u_char *, int)); /* Nak some CIs */ | |
87 | static void ipcp_rejci __ARGS((fsm *, u_char *, int)); /* Reject some CIs */ | |
88 | static u_char ipcp_reqci __ARGS((fsm *, u_char *, int *)); /* Check the requested CIs */ | |
89 | static void ipcp_up __ARGS((fsm *)); /* We're UP */ | |
90 | static void ipcp_down __ARGS((fsm *)); /* We're DOWN */ | |
91 | ||
92 | ||
93 | static fsm ipcp_fsm[NPPP]; /* IPCP fsm structure */ | |
94 | ||
95 | static fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */ | |
96 | ipcp_resetci, /* Reset our Configuration Information */ | |
97 | ipcp_cilen, /* Length of our Configuration Information */ | |
98 | ipcp_addci, /* Add our Configuration Information */ | |
99 | ipcp_ackci, /* ACK our Configuration Information */ | |
100 | ipcp_nakci, /* NAK our Configuration Information */ | |
101 | ipcp_rejci, /* Reject our Configuration Information */ | |
102 | ipcp_reqci, /* Request peer's Configuration Information */ | |
103 | ipcp_up, /* Called when fsm reaches OPEN state */ | |
104 | ipcp_down, /* Called when fsm leaves OPEN state */ | |
105 | NULL, /* Called when fsm reaches CLOSED state */ | |
106 | NULL, /* Called when Protocol-Reject received */ | |
107 | NULL /* Retransmission is necessary */ | |
108 | }; | |
109 | ||
110 | char * | |
111 | ip_ntoa(ipaddr) | |
112 | u_long ipaddr; | |
113 | { | |
114 | static char b1[64], b2[64], w = 0; | |
115 | char *b = (w++&1) ? b1 : b2; | |
116 | ||
117 | ipaddr = ntohl(ipaddr); | |
118 | ||
119 | sprintf(b, "%d.%d.%d.%d", | |
120 | (u_char)(ipaddr >> 24), | |
121 | (u_char)(ipaddr >> 16), | |
122 | (u_char)(ipaddr >> 8), | |
123 | (u_char)(ipaddr)); | |
124 | return b; | |
125 | } | |
126 | ||
127 | /* | |
128 | * ipcp_init - Initialize IPCP. | |
129 | */ | |
130 | void | |
131 | ipcp_init(unit) | |
132 | int unit; | |
133 | { | |
134 | fsm *f = &ipcp_fsm[unit]; | |
135 | ipcp_options *wo = &ipcp_wantoptions[unit]; | |
136 | ipcp_options *ao = &ipcp_allowoptions[unit]; | |
137 | ||
138 | f->unit = unit; | |
139 | f->protocol = IPCP; | |
140 | f->timeouttime = DEFTIMEOUT; | |
141 | f->maxconfreqtransmits = DEFMAXCONFIGREQS; | |
142 | f->maxtermtransmits = DEFMAXTERMTRANSMITS; | |
143 | f->maxnakloops = DEFMAXNAKLOOPS; | |
144 | f->callbacks = &ipcp_callbacks; | |
145 | ||
146 | wo->neg_addrs = 1; | |
147 | wo->ouraddr = 0; | |
148 | wo->hisaddr = 0; | |
149 | ||
150 | wo->neg_vj = 1; | |
151 | wo->maxslotindex = MAX_STATES - 1; /* really max index */ | |
152 | wo->cflag = 1; | |
153 | ||
154 | /* max slots and slot-id compression are currently hardwired in */ | |
155 | /* ppp_if.c to 16 and 1, this needs to be changed (among other */ | |
156 | /* things) gmc */ | |
157 | ||
158 | ao->neg_addrs = 1; /* accept old style dual addr */ | |
159 | ao->neg_addr = 1; /* accept new style single addr */ | |
160 | ao->neg_vj = 1; | |
161 | ao->maxslotindex = MAX_STATES - 1; | |
162 | ao->cflag = 1; | |
163 | fsm_init(&ipcp_fsm[unit]); | |
164 | } | |
165 | ||
166 | /* | |
167 | * ipcp_vj_setmode - set option length and option value for vj | |
168 | * compression negotiation frames depending on mode | |
169 | */ | |
170 | ||
171 | void | |
172 | ipcp_vj_setmode(mode) | |
173 | int mode; | |
174 | { | |
175 | vj_mode = mode; | |
176 | ||
177 | switch (vj_mode) { | |
178 | ||
179 | case IPCP_VJMODE_OLD: /* with wrong code (0x0037) */ | |
180 | vj_opt_len = 4; | |
181 | vj_opt_val = IPCP_VJ_COMP_OLD; | |
182 | break; | |
183 | ||
184 | case IPCP_VJMODE_RFC1172: /* as per rfc1172 */ | |
185 | vj_opt_len = 4; | |
186 | vj_opt_val = IPCP_VJ_COMP; | |
187 | break; | |
188 | ||
189 | case IPCP_VJMODE_RFC1332: /* draft mode vj compression */ | |
190 | vj_opt_len = 6; /* negotiation includes values for */ | |
191 | /* maxslot and slot number compression */ | |
192 | vj_opt_val = IPCP_VJ_COMP; | |
193 | break; | |
194 | ||
195 | default: | |
196 | IPCPDEBUG((LOG_WARNING, "Unknown vj compression mode %d. Please report \ | |
197 | this error.", vj_mode)) | |
198 | break; | |
199 | } | |
200 | ||
201 | } | |
202 | /* | |
203 | * ipcp_activeopen - Actively open IPCP. | |
204 | */ | |
205 | void | |
206 | ipcp_activeopen(unit) | |
207 | int unit; | |
208 | { | |
209 | fsm_activeopen(&ipcp_fsm[unit]); | |
210 | } | |
211 | ||
212 | ||
213 | /* | |
214 | * ipcp_passiveopen - Passively open IPCP. | |
215 | */ | |
216 | void ipcp_passiveopen(unit) | |
217 | int unit; | |
218 | { | |
219 | fsm_passiveopen(&ipcp_fsm[unit]); | |
220 | } | |
221 | ||
222 | ||
223 | /* | |
224 | * ipcp_close - Close IPCP. | |
225 | */ | |
226 | void | |
227 | ipcp_close(unit) | |
228 | int unit; | |
229 | { | |
230 | fsm_close(&ipcp_fsm[unit]); | |
231 | } | |
232 | ||
233 | ||
234 | /* | |
235 | * ipcp_lowerup - The lower layer is up. | |
236 | */ | |
237 | void | |
238 | ipcp_lowerup(unit) | |
239 | int unit; | |
240 | { | |
241 | fsm_lowerup(&ipcp_fsm[unit]); | |
242 | } | |
243 | ||
244 | ||
245 | /* | |
246 | * ipcp_lowerdown - The lower layer is down. | |
247 | */ | |
248 | void | |
249 | ipcp_lowerdown(unit) | |
250 | int unit; | |
251 | { | |
252 | fsm_lowerdown(&ipcp_fsm[unit]); | |
253 | } | |
254 | ||
255 | ||
256 | /* | |
257 | * ipcp_input - Input IPCP packet. | |
258 | */ | |
259 | void | |
260 | ipcp_input(unit, p, len) | |
261 | int unit; | |
262 | u_char *p; | |
263 | int len; | |
264 | { | |
265 | fsm_input(&ipcp_fsm[unit], p, len); | |
266 | } | |
267 | ||
268 | ||
269 | /* | |
270 | * ipcp_protrej - A Protocol-Reject was received for IPCP. | |
271 | * | |
272 | * Simply pretend that LCP went down. | |
273 | */ | |
274 | void | |
275 | ipcp_protrej(unit) | |
276 | int unit; | |
277 | { | |
278 | fsm_lowerdown(&ipcp_fsm[unit]); | |
279 | } | |
280 | ||
281 | ||
282 | /* | |
283 | * ipcp_resetci - Reset our CI. | |
284 | */ | |
285 | static void | |
286 | ipcp_resetci(f) | |
287 | fsm *f; | |
288 | { | |
289 | ipcp_gotoptions[f->unit] = ipcp_wantoptions[f->unit]; | |
290 | } | |
291 | ||
292 | ||
293 | /* | |
294 | * ipcp_cilen - Return length of our CI. | |
295 | */ | |
296 | static int | |
297 | ipcp_cilen(f) | |
298 | fsm *f; | |
299 | { | |
300 | ipcp_options *go = &ipcp_gotoptions[f->unit]; | |
301 | ||
302 | ||
303 | #define LENCISHORT(neg) (neg ? vj_opt_len : 0) | |
304 | ||
305 | #define LENCIADDRS(neg) (neg ? 10 : 0) | |
306 | ||
307 | #define LENCIADDR(neg) (neg ? 6 : 0) | |
308 | ||
309 | return (LENCIADDRS(go->neg_addrs) + | |
310 | LENCIADDR(go->neg_addr) + | |
311 | LENCISHORT(go->neg_vj)); | |
312 | } | |
313 | ||
314 | ||
315 | /* | |
316 | * ipcp_addci - Add our desired CIs to a packet. | |
317 | */ | |
318 | static void | |
319 | ipcp_addci(f, ucp) | |
320 | fsm *f; | |
321 | u_char *ucp; | |
322 | { | |
323 | ipcp_options *go = &ipcp_gotoptions[f->unit]; | |
324 | ||
325 | ||
326 | #define ADDCISHORT(opt, neg, val, maxslotindex, cflag) \ | |
327 | if (neg) { \ | |
328 | PUTCHAR(opt, ucp); \ | |
329 | PUTCHAR(vj_opt_len, ucp); \ | |
330 | PUTSHORT(val, ucp); \ | |
331 | if (vj_mode == IPCP_VJMODE_RFC1332) { \ | |
332 | PUTCHAR(maxslotindex, ucp); \ | |
333 | PUTCHAR(cflag, ucp); \ | |
334 | } \ | |
335 | } | |
336 | ||
337 | #define ADDCIADDRS(opt, neg, val1, val2) \ | |
338 | if (neg) { \ | |
339 | u_long l; \ | |
340 | PUTCHAR(opt, ucp); \ | |
341 | PUTCHAR(2 + 2 * sizeof (long), ucp); \ | |
342 | l = ntohl(val1); \ | |
343 | PUTLONG(l, ucp); \ | |
344 | l = ntohl(val2); \ | |
345 | PUTLONG(l, ucp); \ | |
346 | } | |
347 | ||
348 | #define ADDCIADDR(opt, neg, val) \ | |
349 | if (neg) { \ | |
350 | u_long l; \ | |
351 | PUTCHAR(opt, ucp); \ | |
352 | PUTCHAR(2 + sizeof (long), ucp); \ | |
353 | l = ntohl(val); \ | |
354 | PUTLONG(l, ucp); \ | |
355 | } | |
356 | ||
357 | ADDCIADDRS(CI_ADDRS, go->neg_addrs, go->ouraddr, go->hisaddr) | |
358 | ||
359 | ADDCIADDR(CI_ADDR, go->neg_addr, go->ouraddr) | |
360 | ||
361 | ADDCISHORT(CI_COMPRESSTYPE, go->neg_vj, vj_opt_val, | |
362 | go->maxslotindex, go->cflag) | |
363 | } | |
364 | ||
365 | ||
366 | /* | |
367 | * ipcp_ackci - Ack our CIs. | |
368 | * | |
369 | * Returns: | |
370 | * 0 - Ack was bad. | |
371 | * 1 - Ack was good. | |
372 | */ | |
373 | static int | |
374 | ipcp_ackci(f, p, len) | |
375 | fsm *f; | |
376 | u_char *p; | |
377 | int len; | |
378 | { | |
379 | ipcp_options *go = &ipcp_gotoptions[f->unit]; | |
380 | u_short cilen, citype, cishort; | |
381 | u_long cilong; | |
382 | u_char cimaxslotindex, cicflag; | |
383 | /* | |
384 | * CIs must be in exactly the same order that we sent... | |
385 | * Check packet length and CI length at each step. | |
386 | * If we find any deviations, then this packet is bad. | |
387 | */ | |
388 | #define ACKCISHORT(opt, neg, val, maxslotindex, cflag) \ | |
389 | if (neg) { \ | |
390 | if ((len -= vj_opt_len) < 0) \ | |
391 | goto bad; \ | |
392 | GETCHAR(citype, p); \ | |
393 | GETCHAR(cilen, p); \ | |
394 | if (cilen != vj_opt_len || \ | |
395 | citype != opt) \ | |
396 | goto bad; \ | |
397 | GETSHORT(cishort, p); \ | |
398 | if (cishort != val) \ | |
399 | goto bad; \ | |
400 | if (vj_mode == IPCP_VJMODE_RFC1332) { \ | |
401 | GETCHAR(cimaxslotindex, p); \ | |
402 | if (cimaxslotindex > maxslotindex) \ | |
403 | goto bad; \ | |
404 | GETCHAR(cicflag, p); \ | |
405 | if (cicflag != cflag) \ | |
406 | goto bad; \ | |
407 | } \ | |
408 | } | |
409 | ||
410 | #define ACKCIADDRS(opt, neg, val1, val2) \ | |
411 | if (neg) { \ | |
412 | u_long l; \ | |
413 | if ((len -= 2 + 2 * sizeof (long)) < 0) \ | |
414 | goto bad; \ | |
415 | GETCHAR(citype, p); \ | |
416 | GETCHAR(cilen, p); \ | |
417 | if (cilen != 2 + 2 * sizeof (long) || \ | |
418 | citype != opt) \ | |
419 | goto bad; \ | |
420 | GETLONG(l, p); \ | |
421 | cilong = htonl(l); \ | |
422 | if (val1) { \ | |
423 | if (val1 != cilong) \ | |
424 | goto bad; \ | |
425 | } \ | |
426 | else \ | |
427 | val1 = cilong; \ | |
428 | GETLONG(l, p); \ | |
429 | cilong = htonl(l); \ | |
430 | if (val2) { \ | |
431 | if (val2 != cilong) \ | |
432 | goto bad; \ | |
433 | } \ | |
434 | else \ | |
435 | val2 = cilong; \ | |
436 | } | |
437 | ||
438 | #define ACKCIADDR(opt, neg, val) \ | |
439 | if (neg) { \ | |
440 | u_long l; \ | |
441 | if ((len -= 2 + sizeof (long)) < 0) \ | |
442 | goto bad; \ | |
443 | GETCHAR(citype, p); \ | |
444 | GETCHAR(cilen, p); \ | |
445 | if (cilen != 2 + sizeof (long) || \ | |
446 | citype != opt) \ | |
447 | goto bad; \ | |
448 | GETLONG(l, p); \ | |
449 | cilong = htonl(l); \ | |
450 | if (val) { \ | |
451 | if (val != cilong) \ | |
452 | goto bad; \ | |
453 | } \ | |
454 | else \ | |
455 | val = cilong; \ | |
456 | } | |
457 | ||
458 | ACKCIADDRS(CI_ADDRS, go->neg_addrs, go->ouraddr, go->hisaddr) | |
459 | ACKCIADDR(CI_ADDR, go->neg_addr, go->ouraddr) | |
460 | ACKCISHORT(CI_COMPRESSTYPE, go->neg_vj, vj_opt_val, go->maxslotindex, go->cflag) | |
461 | /* | |
462 | * If there are any remaining CIs, then this packet is bad. | |
463 | */ | |
464 | if (len != 0) | |
465 | goto bad; | |
466 | return (1); | |
467 | ||
468 | bad: | |
469 | IPCPDEBUG((LOG_INFO, "ipcp_ackci: received bad Ack!")); | |
470 | ||
471 | if (vj_mode == IPCP_VJMODE_RFC1332 ) | |
472 | IPCPDEBUG((LOG_INFO, "ipcp_ackci: citype %d, cilen %l", | |
473 | citype, cilen)); | |
474 | ||
475 | if (citype == CI_COMPRESSTYPE) { | |
476 | IPCPDEBUG((LOG_INFO, "ipcp_ackci: compress_type %d", cishort)); | |
477 | if (vj_mode == IPCP_VJMODE_RFC1332) | |
478 | IPCPDEBUG((LOG_INFO, ", maxslotindex %d, cflag %d", | |
479 | cishort, cimaxslotindex, cicflag)); | |
480 | } | |
481 | return (0); | |
482 | } | |
483 | ||
484 | /* | |
485 | * ipcp_nakci - NAK some of our CIs. | |
486 | * | |
487 | * Returns: | |
488 | * 0 - Nak was bad. | |
489 | * 1 - Nak was good. | |
490 | */ | |
491 | static void | |
492 | ipcp_nakci(f, p, len) | |
493 | fsm *f; | |
494 | u_char *p; | |
495 | int len; | |
496 | { | |
497 | ipcp_options *go = &ipcp_gotoptions[f->unit]; | |
498 | u_char cimaxslotindex, cicflag; | |
499 | u_short cishort; | |
500 | u_long ciaddr1, ciaddr2; | |
501 | ||
502 | /* | |
503 | * Any Nak'd CIs must be in exactly the same order that we sent. | |
504 | * Check packet length and CI length at each step. | |
505 | * If we find any deviations, then this packet is bad. | |
506 | */ | |
507 | #define NAKCISHORT(opt, neg, code) \ | |
508 | if (neg && \ | |
509 | len >= vj_opt_len && \ | |
510 | p[1] == vj_opt_len && \ | |
511 | p[0] == opt) { \ | |
512 | len -= vj_opt_len; \ | |
513 | INCPTR(2, p); \ | |
514 | GETSHORT(cishort, p); \ | |
515 | if (vj_mode == IPCP_VJMODE_RFC1332) { \ | |
516 | GETCHAR(cimaxslotindex, p); \ | |
517 | GETCHAR(cicflag, p); \ | |
518 | } \ | |
519 | code \ | |
520 | } | |
521 | ||
522 | #define NAKCIADDRS(opt, neg, code) \ | |
523 | if (neg && \ | |
524 | len >= 2 + 2 * sizeof (long) && \ | |
525 | p[1] == 2 + 2 * sizeof (long) && \ | |
526 | p[0] == opt) { \ | |
527 | u_long l; \ | |
528 | len -= 2 + 2 * sizeof (long); \ | |
529 | INCPTR(2, p); \ | |
530 | GETLONG(l, p); \ | |
531 | ciaddr1 = htonl(l); \ | |
532 | GETLONG(l, p); \ | |
533 | ciaddr2 = htonl(l); \ | |
534 | code \ | |
535 | } | |
536 | ||
537 | #define NAKCIADDR(opt, neg, code) \ | |
538 | if (neg && \ | |
539 | len >= 2 + sizeof (long) && \ | |
540 | p[1] == 2 + sizeof (long) && \ | |
541 | p[0] == opt) { \ | |
542 | u_long l; \ | |
543 | len -= 2 + sizeof (long); \ | |
544 | INCPTR(2, p); \ | |
545 | GETLONG(l, p); \ | |
546 | ciaddr1 = htonl(l); \ | |
547 | code \ | |
548 | } | |
549 | ||
550 | NAKCIADDRS(CI_ADDRS, go->neg_addrs, | |
551 | if (!go->ouraddr) { /* Didn't know our address? */ | |
552 | syslog(LOG_INFO, "local IP address %s", ip_ntoa(ciaddr1)); | |
553 | go->ouraddr = ciaddr1; | |
554 | } | |
555 | if (ciaddr2) { /* Does he know his? */ | |
556 | go->hisaddr = ciaddr2; | |
557 | syslog(LOG_INFO, "remote IP address %s", ip_ntoa(ciaddr2)); | |
558 | } | |
559 | ) | |
560 | ||
561 | NAKCIADDR(CI_ADDR, go->neg_addr, | |
562 | logf(LOG_INFO, "acquired IP address %s", ip_ntoa(ciaddr1)); | |
563 | if (!go->ouraddr) { /* Didn't know our address? */ | |
564 | go->ouraddr = ciaddr1; | |
565 | syslog(LOG_INFO, "remote IP address %s", ip_ntoa(ciaddr1)); | |
566 | } | |
567 | ) | |
568 | ||
569 | NAKCISHORT(CI_COMPRESSTYPE, go->neg_vj, | |
570 | if (cishort != vj_opt_val) | |
571 | goto bad; | |
572 | go->maxslotindex = cimaxslotindex; /* this is what it */ | |
573 | go->cflag = cicflag; /* wants */ | |
574 | ||
575 | ) | |
576 | /* | |
577 | * If there are any remaining CIs, then this packet is bad. | |
578 | */ | |
579 | if (len == 0) | |
580 | return; | |
581 | bad: | |
582 | IPCPDEBUG((LOG_INFO, "ipcp_nakci: received bad Nak!")); | |
583 | } | |
584 | ||
585 | ||
586 | /* | |
587 | * ipcp_rejci - Reject some of our CIs. | |
588 | */ | |
589 | static void | |
590 | ipcp_rejci(f, p, len) | |
591 | fsm *f; | |
592 | u_char *p; | |
593 | int len; | |
594 | { | |
595 | ipcp_options *go = &ipcp_gotoptions[f->unit]; | |
596 | u_char cimaxslotindex, ciflag; | |
597 | u_short cishort; | |
598 | u_long cilong; | |
599 | ||
600 | /* | |
601 | * Any Rejected CIs must be in exactly the same order that we sent. | |
602 | * Check packet length and CI length at each step. | |
603 | * If we find any deviations, then this packet is bad. | |
604 | */ | |
605 | #define REJCISHORT(opt, neg, val, maxslot, cflag) \ | |
606 | if (neg && \ | |
607 | len >= vj_opt_len && \ | |
608 | p[1] == vj_opt_len && \ | |
609 | p[0] == opt) { \ | |
610 | len -= vj_opt_len; \ | |
611 | INCPTR(2, p); \ | |
612 | GETSHORT(cishort, p); \ | |
613 | /* Check rejected value. */ \ | |
614 | if (cishort != val) \ | |
615 | goto bad; \ | |
616 | if (vj_mode == IPCP_VJMODE_RFC1332) { \ | |
617 | GETCHAR(cimaxslotindex, p); \ | |
618 | if (cimaxslotindex != maxslot) \ | |
619 | goto bad; \ | |
620 | GETCHAR(ciflag, p); \ | |
621 | if (ciflag != cflag) \ | |
622 | goto bad; \ | |
623 | } \ | |
624 | neg = 0; \ | |
625 | } | |
626 | ||
627 | #define REJCIADDRS(opt, neg, val1, val2) \ | |
628 | if (neg && \ | |
629 | len >= 2 + 2 * sizeof (long) && \ | |
630 | p[1] == 2 + 2 * sizeof (long) && \ | |
631 | p[0] == opt) { \ | |
632 | u_long l; \ | |
633 | len -= 2 + 2 * sizeof (long); \ | |
634 | INCPTR(2, p); \ | |
635 | GETLONG(l, p); \ | |
636 | cilong = htonl(l); \ | |
637 | /* Check rejected value. */ \ | |
638 | if (cilong != val2) \ | |
639 | goto bad; \ | |
640 | GETLONG(l, p); \ | |
641 | cilong = htonl(l); \ | |
642 | /* Check rejected value. */ \ | |
643 | if (cilong != val1) \ | |
644 | goto bad; \ | |
645 | neg = 0; \ | |
646 | } | |
647 | ||
648 | #define REJCIADDR(opt, neg, val) \ | |
649 | if (neg && \ | |
650 | len >= 2 + sizeof (long) && \ | |
651 | p[1] == 2 + sizeof (long) && \ | |
652 | p[0] == opt) { \ | |
653 | u_long l; \ | |
654 | len -= 2 + sizeof (long); \ | |
655 | INCPTR(2, p); \ | |
656 | GETLONG(l, p); \ | |
657 | cilong = htonl(l); \ | |
658 | /* Check rejected value. */ \ | |
659 | if (cilong != val) \ | |
660 | goto bad; \ | |
661 | neg = 0; \ | |
662 | } | |
663 | ||
664 | REJCIADDRS(CI_ADDRS, go->neg_addrs, go->ouraddr, go->hisaddr) | |
665 | ||
666 | REJCIADDR(CI_ADDR, go->neg_addr, go->ouraddr) | |
667 | ||
668 | REJCISHORT(CI_COMPRESSTYPE, go->neg_vj, vj_opt_val, go->maxslotindex, go->cflag) | |
669 | ||
670 | /* | |
671 | * If there are any remaining CIs, then this packet is bad. | |
672 | */ | |
673 | if (len == 0) | |
674 | return; | |
675 | ||
676 | bad: | |
677 | IPCPDEBUG((LOG_INFO, "ipcp_rejci: received bad Reject!")); | |
678 | } | |
679 | ||
680 | ||
681 | /* | |
682 | * ipcp_reqci - Check the peer's requested CIs and send appropriate response. | |
683 | * | |
684 | * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified | |
685 | * appropriately. | |
686 | */ | |
687 | static u_char | |
688 | ipcp_reqci(f, inp, len) | |
689 | fsm *f; | |
690 | u_char *inp; /* Requested CIs */ | |
691 | int *len; /* Length of requested CIs */ | |
692 | { | |
693 | ipcp_options *wo = &ipcp_wantoptions[f->unit]; | |
694 | ipcp_options *ho = &ipcp_hisoptions[f->unit]; | |
695 | ipcp_options *ao = &ipcp_allowoptions[f->unit]; | |
696 | ipcp_options *go = &ipcp_gotoptions[f->unit]; | |
697 | u_char *cip; /* Pointer to Current CI */ | |
698 | u_short cilen, citype; /* Parsed len, type */ | |
699 | u_short cishort; /* Parsed short value */ | |
700 | u_long tl, ciaddr1, ciaddr2; /* Parsed address values */ | |
701 | int rc = CONFACK; /* Final packet return code */ | |
702 | int orc; /* Individual option return code */ | |
703 | u_char *p = inp; /* Pointer to next char to parse */ | |
704 | u_char *ucp = inp; /* Pointer to current output char */ | |
705 | int l = *len; /* Length left */ | |
706 | u_char maxslotindex, cflag; | |
707 | ||
708 | /* | |
709 | * Reset all his options. | |
710 | */ | |
711 | ho->neg_addrs = 0; | |
712 | ho->neg_vj = 0; | |
713 | ho->maxslotindex = 0; | |
714 | ho->cflag = 0; | |
715 | ||
716 | /* | |
717 | * Process all his options. | |
718 | */ | |
719 | while (l) { | |
720 | orc = CONFACK; /* Assume success */ | |
721 | cip = p; /* Remember begining of CI */ | |
722 | if (l < 2 || /* Not enough data for CI header or */ | |
723 | p[1] < 2 || /* CI length too small or */ | |
724 | p[1] > l) { /* CI length too big? */ | |
725 | IPCPDEBUG((LOG_INFO, "ipcp_reqci: bad CI length!")); | |
726 | orc = CONFREJ; /* Reject bad CI */ | |
727 | cilen = l; /* Reject till end of packet */ | |
728 | l = 0; /* Don't loop again */ | |
729 | goto endswitch; | |
730 | } | |
731 | GETCHAR(citype, p); /* Parse CI type */ | |
732 | GETCHAR(cilen, p); /* Parse CI length */ | |
733 | l -= cilen; /* Adjust remaining length */ | |
734 | cilen -= 2; /* Adjust cilen to just data */ | |
735 | ||
736 | switch (citype) { /* Check CI type */ | |
737 | case CI_ADDRS: | |
738 | logf(LOG_INFO, "ipcp: received ADDRS "); | |
739 | if (!ao->neg_addrs || | |
740 | cilen != 2 * sizeof (long)) | |
741 | { /* Check CI length */ | |
742 | INCPTR(cilen, p); /* Skip rest of CI */ | |
743 | orc = CONFREJ; /* Reject CI */ | |
744 | break; | |
745 | } | |
746 | ||
747 | /* | |
748 | * If he has no address, or if we both have his address but | |
749 | * disagree about it, then NAK it with our idea. | |
750 | * In particular, if we don't know his address, but he does, | |
751 | * then accept it. | |
752 | */ | |
753 | GETLONG(tl, p); /* Parse source address (his) */ | |
754 | ciaddr1 = htonl(tl); | |
755 | if (!ciaddr1 || | |
756 | (wo->neg_addrs && wo->hisaddr && ciaddr1 != wo->hisaddr)) | |
757 | { | |
758 | orc = CONFNAK; | |
759 | DECPTR(sizeof (long), p); | |
760 | tl = wo->neg_addrs ? ntohl(wo->hisaddr) : 0; | |
761 | PUTLONG(tl, p); | |
762 | } | |
763 | ||
764 | /* | |
765 | * If he doesn't know our address, or if we both have our address | |
766 | * but disagree about it, then NAK it with our idea. | |
767 | */ | |
768 | GETLONG(tl, p); /* Parse desination address (ours) */ | |
769 | ciaddr2 = htonl(tl); | |
770 | logf(LOG_INFO, "(%s:%s)", ip_ntoa(ciaddr1), ip_ntoa(ciaddr2)); | |
771 | if (!ciaddr2 || | |
772 | (wo->neg_addrs && wo->ouraddr && ciaddr2 != wo->ouraddr)) | |
773 | { | |
774 | orc = CONFNAK; | |
775 | DECPTR(sizeof (long), p); | |
776 | tl = ntohl(wo->ouraddr); | |
777 | PUTLONG(tl, p); | |
778 | } | |
779 | if (orc == CONFNAK) | |
780 | break; | |
781 | ||
782 | /* XXX ho or go? */ | |
783 | ho->neg_addrs = 1; | |
784 | ho->hisaddr = ciaddr1; | |
785 | ho->ouraddr = ciaddr2; | |
786 | break; | |
787 | ||
788 | case CI_ADDR: | |
789 | logf(LOG_INFO, "ipcp: received ADDR "); | |
790 | go->got_addr = 1; | |
791 | go->neg_addrs = 0; | |
792 | go->neg_addr = 1; | |
793 | ||
794 | if (!ao->neg_addr || | |
795 | cilen != sizeof (long)) { /* Check CI length */ | |
796 | INCPTR(cilen, p); /* Skip rest of CI */ | |
797 | orc = CONFREJ; /* Reject CI */ | |
798 | break; | |
799 | } | |
800 | ||
801 | /* | |
802 | * If he has no address, or if we both have his address but | |
803 | * disagree about it, then NAK it with our idea. | |
804 | * In particular, if we don't know his address, but he does, | |
805 | * then accept it. | |
806 | */ | |
807 | GETLONG(tl, p); /* Parse source address (his) */ | |
808 | ciaddr1 = htonl(tl); | |
809 | logf(LOG_INFO, "(%s)", ip_ntoa(ciaddr1)); | |
810 | if (!ciaddr1 || | |
811 | (wo->neg_addr && wo->hisaddr && ciaddr1 != wo->hisaddr)) { | |
812 | orc = CONFNAK; | |
813 | DECPTR(sizeof (long), p); | |
814 | tl = wo->neg_addr ? ntohl(wo->hisaddr) : 0; | |
815 | PUTLONG(tl, p); | |
816 | } | |
817 | ||
818 | if (orc == CONFNAK) | |
819 | break; | |
820 | ||
821 | /* XXX ho or go? */ | |
822 | ho->neg_addr = 1; | |
823 | ho->hisaddr = ciaddr1; | |
824 | break; | |
825 | ||
826 | case CI_COMPRESSTYPE: | |
827 | logf(LOG_INFO, "ipcp: received COMPRESSTYPE "); | |
828 | if (!ao->neg_vj || | |
829 | cilen != (vj_opt_len - 2)) { | |
830 | INCPTR(cilen, p); | |
831 | orc = CONFREJ; | |
832 | break; | |
833 | } | |
834 | GETSHORT(cishort, p); | |
835 | logf(LOG_INFO, "(%d)", cishort); | |
836 | ||
837 | /* | |
838 | * Compresstype must be vj_opt_val. | |
839 | */ | |
840 | if (cishort != vj_opt_val) { | |
841 | DECPTR(sizeof (short), p); | |
842 | orc = CONFNAK; | |
843 | PUTSHORT(vj_opt_val, p); | |
844 | break; | |
845 | } | |
846 | ho->neg_vj = 1; | |
847 | if (vj_mode == IPCP_VJMODE_RFC1332) { | |
848 | GETCHAR(maxslotindex, p); | |
849 | if (maxslotindex > wo->maxslotindex) { | |
850 | DECPTR(1, p); | |
851 | orc = CONFNAK; | |
852 | PUTCHAR(wo->maxslotindex, p); | |
853 | break; | |
854 | } | |
855 | ho->maxslotindex = maxslotindex; | |
856 | ||
857 | GETCHAR(cflag, p); | |
858 | if (cflag != wo->cflag) { | |
859 | DECPTR(1, p); | |
860 | orc = CONFNAK; | |
861 | PUTCHAR(wo->cflag, p); | |
862 | break; | |
863 | } | |
864 | ho->cflag = wo->cflag; | |
865 | } | |
866 | break; | |
867 | ||
868 | default: | |
869 | INCPTR(cilen, p); | |
870 | orc = CONFREJ; | |
871 | break; | |
872 | } | |
873 | cilen += 2; /* Adjust cilen whole CI */ | |
874 | ||
875 | endswitch: | |
876 | logf(LOG_INFO, " (%s)\n", | |
877 | orc == CONFACK ? "ACK" : (orc == CONFNAK ? "NAK" : "Reject")); | |
878 | ||
879 | if (orc == CONFACK && /* Good CI */ | |
880 | rc != CONFACK) /* but prior CI wasnt? */ | |
881 | continue; /* Don't send this one */ | |
882 | ||
883 | if (orc == CONFNAK) { /* Nak this CI? */ | |
884 | if (rc == CONFREJ) /* Rejecting prior CI? */ | |
885 | continue; /* Don't send this one */ | |
886 | if (rc == CONFACK) { /* Ack'd all prior CIs? */ | |
887 | rc = CONFNAK; /* Not anymore... */ | |
888 | ucp = inp; /* Backup */ | |
889 | } | |
890 | } | |
891 | ||
892 | if (orc == CONFREJ && /* Reject this CI */ | |
893 | rc != CONFREJ) { /* but no prior ones? */ | |
894 | rc = CONFREJ; | |
895 | ucp = inp; /* Backup */ | |
896 | } | |
897 | ||
898 | /* Need to move CI? */ | |
899 | if (ucp != cip) | |
900 | /* Move it */ | |
901 | memcpy(ucp, cip, (size_t)cilen); | |
902 | ||
903 | /* Update output pointer */ | |
904 | INCPTR(cilen, ucp); | |
905 | } | |
906 | ||
907 | /* | |
908 | * XXX If we wanted to send additional NAKs (for unsent CIs), the | |
909 | * code would go here. This must be done with care since it might | |
910 | * require a longer packet than we received. | |
911 | */ | |
912 | ||
913 | *len = ucp - inp; /* Compute output length */ | |
914 | ||
915 | syslog(LOG_INFO, "ipcp: returning Configure-%s", | |
916 | rc == CONFACK ? "ACK" : | |
917 | rc == CONFNAK ? "NAK" : "Reject"); | |
918 | ||
919 | return (rc); /* Return final code */ | |
920 | } | |
921 | ||
922 | ||
923 | /* | |
924 | * ipcp_up - IPCP has come UP. | |
925 | */ | |
926 | static void | |
927 | ipcp_up(f) | |
928 | fsm *f; | |
929 | { | |
930 | u_long mask; | |
931 | ||
932 | syslog(LOG_INFO, "ipcp: up"); | |
933 | ||
934 | if (ipcp_hisoptions[f->unit].hisaddr == 0) | |
935 | ipcp_hisoptions[f->unit].hisaddr = ipcp_wantoptions[f->unit].hisaddr; | |
936 | ||
937 | syslog(LOG_INFO, "local IP address %s", | |
938 | ip_ntoa(ipcp_gotoptions[f->unit].ouraddr)); | |
939 | syslog(LOG_INFO, "remote IP address %s", | |
940 | ip_ntoa(ipcp_hisoptions[f->unit].hisaddr)); | |
941 | ||
942 | SIFADDR(f->unit, ipcp_gotoptions[f->unit].ouraddr, | |
943 | ipcp_hisoptions[f->unit].hisaddr); | |
944 | ||
945 | /* set new netmask if specified */ | |
946 | mask = GetMask(ipcp_gotoptions[f->unit].ouraddr); | |
947 | if (mask) | |
948 | SIFMASK(f->unit, mask); | |
949 | ||
950 | /* set tcp compression */ | |
951 | SIFVJCOMP(f->unit, ipcp_hisoptions[f->unit].neg_vj); | |
952 | } | |
953 | ||
954 | ||
955 | /* | |
956 | * ipcp_down - IPCP has gone DOWN. | |
957 | * | |
958 | * Alert other protocols. | |
959 | */ | |
960 | static void | |
961 | ipcp_down(f) | |
962 | fsm *f; | |
963 | { | |
964 | syslog(LOG_INFO, "ipcp: down"); | |
965 | ||
966 | CIFADDR(f->unit, ipcp_gotoptions[f->unit].ouraddr, | |
967 | ipcp_hisoptions[f->unit].hisaddr); | |
968 | } |