]>
Commit | Line | Data |
---|---|---|
f47781d8 MP |
1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
2 | ||
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright (C) 2014 Tom Gundersen | |
7 | ||
8 | systemd is free software; you can redistribute it and/or modify it | |
9 | under the terms of the GNU Lesser General Public License as published by | |
10 | the Free Software Foundation; either version 2.1 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | systemd is distributed in the hope that it will be useful, but | |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | Lesser General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU Lesser General Public License | |
19 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
20 | ***/ | |
21 | ||
22 | /* See RFC 2516 */ | |
23 | ||
24 | #include <sys/ioctl.h> | |
25 | #include <linux/ppp_defs.h> | |
26 | #include <linux/ppp-ioctl.h> | |
27 | #include <net/if.h> | |
28 | #include <netinet/in.h> | |
29 | #include <linux/if_pppox.h> | |
30 | ||
31 | #include "sd-pppoe.h" | |
32 | ||
33 | #include "event-util.h" | |
34 | ||
35 | #include "util.h" | |
e3bff60a | 36 | #include "random-util.h" |
f47781d8 MP |
37 | #include "socket-util.h" |
38 | #include "async.h" | |
f47781d8 MP |
39 | #include "utf8.h" |
40 | ||
41 | #define PPPOE_MAX_PACKET_SIZE 1484 | |
42 | #define PPPOE_MAX_PADR_RESEND 16 | |
43 | ||
44 | /* TODO: move this to socket-util.h without getting into | |
45 | * a mess with the includes */ | |
46 | union sockaddr_union_pppox { | |
47 | struct sockaddr sa; | |
48 | struct sockaddr_pppox pppox; | |
49 | }; | |
50 | ||
51 | typedef enum PPPoEState { | |
52 | PPPOE_STATE_INITIALIZING, | |
53 | PPPOE_STATE_REQUESTING, | |
54 | PPPOE_STATE_RUNNING, | |
55 | PPPOE_STATE_STOPPED, | |
56 | _PPPOE_STATE_MAX, | |
57 | _PPPOE_STATE_INVALID = -1, | |
58 | } PPPoEState; | |
59 | ||
60 | typedef struct PPPoETags { | |
61 | char *service_name; | |
62 | char *ac_name; | |
63 | uint8_t *host_uniq; | |
64 | size_t host_uniq_len; | |
65 | uint8_t *cookie; | |
66 | size_t cookie_len; | |
67 | } PPPoETags; | |
68 | ||
69 | struct sd_pppoe { | |
d9dfd233 | 70 | unsigned n_ref; |
f47781d8 MP |
71 | |
72 | PPPoEState state; | |
73 | uint64_t host_uniq; | |
74 | ||
75 | int ifindex; | |
76 | char *ifname; | |
77 | ||
78 | sd_event *event; | |
79 | int event_priority; | |
80 | int fd; | |
81 | sd_event_source *io; | |
82 | sd_event_source *timeout; | |
83 | int padr_resend_count; | |
84 | ||
85 | char *service_name; | |
86 | struct ether_addr peer_mac; | |
87 | be16_t session_id; | |
88 | ||
89 | int pppoe_fd; | |
90 | int channel; | |
91 | ||
92 | sd_pppoe_cb_t cb; | |
93 | void *userdata; | |
94 | ||
95 | PPPoETags tags; | |
96 | }; | |
97 | ||
98 | #define PPPOE_PACKET_LENGTH(header) \ | |
99 | be16toh((header)->length) | |
100 | ||
101 | #define PPPOE_PACKET_TAIL(packet) \ | |
102 | (struct pppoe_tag*)((uint8_t*)(packet) + sizeof(struct pppoe_hdr) + PPPOE_PACKET_LENGTH(packet)) | |
103 | ||
104 | #define PPPOE_TAG_LENGTH(tag) \ | |
105 | be16toh((tag)->tag_len) | |
106 | ||
107 | #define PPPOE_TAG_TYPE(tag) \ | |
108 | (tag)->tag_type | |
109 | ||
110 | #define PPPOE_TAG_NEXT(tag) \ | |
111 | (struct pppoe_tag *)((uint8_t *)(tag) + sizeof(struct pppoe_tag) + PPPOE_TAG_LENGTH(tag)) | |
112 | ||
113 | #define PPPOE_TAGS_FOREACH(tag, header) \ | |
114 | for (tag = (header)->tag; \ | |
115 | ((uint8_t *)(tag) + sizeof(struct pppoe_tag) < (uint8_t*)PPPOE_PACKET_TAIL(header)) && \ | |
116 | (PPPOE_TAG_NEXT(tag) <= PPPOE_PACKET_TAIL(header)) && \ | |
117 | (tag >= (header)->tag) && \ | |
118 | (PPPOE_TAG_TYPE(tag) != PTT_EOL); \ | |
119 | tag = PPPOE_TAG_NEXT(tag)) | |
120 | ||
121 | static void pppoe_tags_clear(PPPoETags *tags) { | |
122 | free(tags->service_name); | |
123 | free(tags->ac_name); | |
124 | free(tags->host_uniq); | |
125 | free(tags->cookie); | |
126 | ||
127 | zero(*tags); | |
128 | } | |
129 | ||
130 | int sd_pppoe_set_ifindex(sd_pppoe *ppp, int ifindex) { | |
131 | assert_return(ppp, -EINVAL); | |
132 | assert_return(ifindex > 0, -EINVAL); | |
133 | ||
134 | ppp->ifindex = ifindex; | |
135 | ||
136 | return 0; | |
137 | } | |
138 | ||
139 | int sd_pppoe_set_ifname(sd_pppoe *ppp, const char *ifname) { | |
140 | char *name; | |
141 | ||
142 | assert_return(ppp, -EINVAL); | |
143 | assert_return(ifname, -EINVAL); | |
144 | ||
145 | if (strlen(ifname) > IFNAMSIZ) | |
146 | return -EINVAL; | |
147 | ||
148 | name = strdup(ifname); | |
149 | if (!name) | |
150 | return -ENOMEM; | |
151 | ||
152 | free(ppp->ifname); | |
153 | ppp->ifname = name; | |
154 | ||
155 | return 0; | |
156 | } | |
157 | ||
158 | int sd_pppoe_set_service_name(sd_pppoe *ppp, const char *service_name) { | |
159 | _cleanup_free_ char *name = NULL; | |
160 | ||
161 | assert_return(ppp, -EINVAL); | |
162 | ||
163 | if (service_name) { | |
164 | name = strdup(service_name); | |
165 | if (!name) | |
166 | return -ENOMEM; | |
167 | } | |
168 | ||
169 | free(ppp->service_name); | |
170 | ppp->service_name = name; | |
171 | name = NULL; | |
172 | ||
173 | return 0; | |
174 | } | |
175 | ||
176 | int sd_pppoe_attach_event(sd_pppoe *ppp, sd_event *event, int priority) { | |
177 | int r; | |
178 | ||
179 | assert_return(ppp, -EINVAL); | |
180 | assert_return(!ppp->event, -EBUSY); | |
181 | ||
182 | if (event) | |
183 | ppp->event = sd_event_ref(event); | |
184 | else { | |
185 | r = sd_event_default(&ppp->event); | |
186 | if (r < 0) | |
187 | return r; | |
188 | } | |
189 | ||
190 | ppp->event_priority = priority; | |
191 | ||
192 | return 0; | |
193 | } | |
194 | ||
195 | int sd_pppoe_detach_event(sd_pppoe *ppp) { | |
196 | assert_return(ppp, -EINVAL); | |
197 | ||
198 | ppp->event = sd_event_unref(ppp->event); | |
199 | ||
200 | return 0; | |
201 | } | |
202 | ||
203 | sd_pppoe *sd_pppoe_ref(sd_pppoe *ppp) { | |
d9dfd233 MP |
204 | |
205 | if (!ppp) | |
206 | return NULL; | |
207 | ||
208 | assert(ppp->n_ref > 0); | |
209 | ppp->n_ref++; | |
f47781d8 MP |
210 | |
211 | return ppp; | |
212 | } | |
213 | ||
214 | sd_pppoe *sd_pppoe_unref(sd_pppoe *ppp) { | |
f47781d8 | 215 | |
d9dfd233 MP |
216 | if (!ppp) |
217 | return NULL; | |
218 | ||
219 | assert(ppp->n_ref > 0); | |
220 | ppp->n_ref--; | |
221 | ||
222 | if (ppp->n_ref > 0) | |
223 | return NULL; | |
224 | ||
225 | pppoe_tags_clear(&ppp->tags); | |
226 | free(ppp->ifname); | |
227 | free(ppp->service_name); | |
228 | sd_pppoe_stop(ppp); | |
229 | sd_pppoe_detach_event(ppp); | |
230 | ||
231 | free(ppp); | |
f47781d8 MP |
232 | return NULL; |
233 | } | |
234 | ||
235 | int sd_pppoe_new (sd_pppoe **ret) { | |
236 | sd_pppoe *ppp; | |
237 | ||
238 | assert_return(ret, -EINVAL); | |
239 | ||
240 | ppp = new0(sd_pppoe, 1); | |
241 | if (!ppp) | |
242 | return -ENOMEM; | |
243 | ||
d9dfd233 | 244 | ppp->n_ref = 1; |
f47781d8 MP |
245 | ppp->state = _PPPOE_STATE_INVALID; |
246 | ppp->ifindex = -1; | |
247 | ppp->fd = -1; | |
248 | ppp->pppoe_fd = -1; | |
249 | ppp->padr_resend_count = PPPOE_MAX_PADR_RESEND; | |
250 | ||
251 | *ret = ppp; | |
252 | ||
253 | return 0; | |
254 | } | |
255 | ||
256 | int sd_pppoe_get_channel(sd_pppoe *ppp, int *channel) { | |
257 | assert_return(ppp, -EINVAL); | |
258 | assert_return(channel, -EINVAL); | |
259 | assert_return(ppp->pppoe_fd != -1, -EUNATCH); | |
260 | assert_return(ppp->state == PPPOE_STATE_RUNNING, -EUNATCH); | |
261 | ||
262 | *channel = ppp->channel; | |
263 | ||
264 | return 0; | |
265 | } | |
266 | ||
267 | int sd_pppoe_set_callback(sd_pppoe *ppp, sd_pppoe_cb_t cb, void *userdata) { | |
268 | assert_return(ppp, -EINVAL); | |
269 | ||
270 | ppp->cb = cb; | |
271 | ppp->userdata = userdata; | |
272 | ||
273 | return 0; | |
274 | } | |
275 | ||
276 | static void pppoe_tag_append(struct pppoe_hdr *packet, size_t packet_size, be16_t tag_type, const void *tag_data, uint16_t tag_len) { | |
277 | struct pppoe_tag *tag; | |
278 | ||
279 | assert(packet); | |
280 | assert(sizeof(struct pppoe_hdr) + PPPOE_PACKET_LENGTH(packet) + sizeof(struct pppoe_tag) + tag_len <= packet_size); | |
281 | assert(!(!tag_data ^ !tag_len)); | |
282 | ||
283 | tag = PPPOE_PACKET_TAIL(packet); | |
284 | ||
285 | tag->tag_len = htobe16(tag_len); | |
286 | tag->tag_type = tag_type; | |
287 | if (tag_data) | |
288 | memcpy(tag->tag_data, tag_data, tag_len); | |
289 | ||
290 | packet->length = htobe16(PPPOE_PACKET_LENGTH(packet) + sizeof(struct pppoe_tag) + tag_len); | |
291 | } | |
292 | ||
293 | static int pppoe_send(sd_pppoe *ppp, uint8_t code) { | |
294 | union sockaddr_union link = { | |
295 | .ll = { | |
296 | .sll_family = AF_PACKET, | |
297 | .sll_protocol = htons(ETH_P_PPP_DISC), | |
298 | .sll_halen = ETH_ALEN, | |
299 | }, | |
300 | }; | |
301 | _cleanup_free_ struct pppoe_hdr *packet = NULL; | |
302 | int r; | |
303 | ||
304 | assert(ppp); | |
305 | assert(ppp->fd != -1); | |
306 | assert(IN_SET(code, PADI_CODE, PADR_CODE, PADT_CODE)); | |
307 | ||
308 | link.ll.sll_ifindex = ppp->ifindex; | |
309 | if (code == PADI_CODE) | |
310 | memset(&link.ll.sll_addr, 0xff, ETH_ALEN); | |
311 | else | |
312 | memcpy(&link.ll.sll_addr, &ppp->peer_mac, ETH_ALEN); | |
313 | ||
314 | packet = malloc0(PPPOE_MAX_PACKET_SIZE); | |
315 | if (!packet) | |
316 | return -ENOMEM; | |
317 | ||
318 | packet->ver = 0x1; | |
319 | packet->type = 0x1; | |
320 | packet->code = code; | |
321 | if (code == PADT_CODE) | |
322 | packet->sid = ppp->session_id; | |
323 | ||
324 | /* Service-Name */ | |
325 | pppoe_tag_append(packet, PPPOE_MAX_PACKET_SIZE, PTT_SRV_NAME, | |
326 | ppp->service_name, ppp->service_name ? strlen(ppp->service_name) : 0); | |
327 | ||
328 | /* AC-Cookie */ | |
329 | if (code == PADR_CODE && ppp->tags.cookie) | |
330 | pppoe_tag_append(packet, PPPOE_MAX_PACKET_SIZE, PTT_AC_COOKIE, | |
331 | ppp->tags.cookie, ppp->tags.cookie_len); | |
332 | ||
333 | /* Host-Uniq */ | |
334 | if (code != PADT_CODE) { | |
335 | ppp->host_uniq = random_u64(); | |
336 | ||
337 | pppoe_tag_append(packet, PPPOE_MAX_PACKET_SIZE, PTT_HOST_UNIQ, | |
338 | &ppp->host_uniq, sizeof(ppp->host_uniq)); | |
339 | } | |
340 | ||
341 | r = sendto(ppp->fd, packet, sizeof(struct pppoe_hdr) + PPPOE_PACKET_LENGTH(packet), | |
342 | 0, &link.sa, sizeof(link.ll)); | |
343 | if (r < 0) | |
344 | return -errno; | |
345 | ||
346 | return 0; | |
347 | } | |
348 | ||
349 | static int pppoe_timeout(sd_event_source *s, uint64_t usec, void *userdata); | |
350 | ||
351 | static int pppoe_arm_timeout(sd_pppoe *ppp) { | |
352 | _cleanup_event_source_unref_ sd_event_source *timeout = NULL; | |
e3bff60a | 353 | usec_t next_timeout = 0; |
f47781d8 MP |
354 | int r; |
355 | ||
356 | assert(ppp); | |
357 | ||
358 | r = sd_event_now(ppp->event, clock_boottime_or_monotonic(), &next_timeout); | |
13d276d0 | 359 | if (r < 0) |
f47781d8 MP |
360 | return r; |
361 | ||
362 | next_timeout += 500 * USEC_PER_MSEC; | |
363 | ||
364 | r = sd_event_add_time(ppp->event, &timeout, clock_boottime_or_monotonic(), next_timeout, | |
365 | 10 * USEC_PER_MSEC, pppoe_timeout, ppp); | |
366 | if (r < 0) | |
367 | return r; | |
368 | ||
369 | r = sd_event_source_set_priority(timeout, ppp->event_priority); | |
370 | if (r < 0) | |
371 | return r; | |
372 | ||
373 | sd_event_source_unref(ppp->timeout); | |
374 | ppp->timeout = timeout; | |
375 | timeout = NULL; | |
376 | ||
377 | return 0; | |
378 | } | |
379 | ||
380 | static int pppoe_send_initiation(sd_pppoe *ppp) { | |
381 | int r; | |
382 | ||
383 | r = pppoe_send(ppp, PADI_CODE); | |
384 | if (r < 0) | |
385 | return r; | |
386 | ||
387 | log_debug("PPPoE: sent DISCOVER (Service-Name: %s)", | |
6300502b | 388 | strna(ppp->service_name)); |
f47781d8 MP |
389 | |
390 | pppoe_arm_timeout(ppp); | |
391 | ||
392 | return r; | |
393 | } | |
394 | ||
395 | static int pppoe_send_request(sd_pppoe *ppp) { | |
396 | int r; | |
397 | ||
398 | r = pppoe_send(ppp, PADR_CODE); | |
399 | if (r < 0) | |
400 | return r; | |
401 | ||
402 | log_debug("PPPoE: sent REQUEST"); | |
403 | ||
404 | ppp->padr_resend_count --; | |
405 | ||
406 | pppoe_arm_timeout(ppp); | |
407 | ||
408 | return 0; | |
409 | } | |
410 | ||
411 | static int pppoe_send_terminate(sd_pppoe *ppp) { | |
412 | int r; | |
413 | ||
414 | r = pppoe_send(ppp, PADT_CODE); | |
415 | if (r < 0) | |
416 | return r; | |
417 | ||
418 | log_debug("PPPoE: sent TERMINATE"); | |
419 | ||
420 | return 0; | |
421 | } | |
422 | ||
423 | static int pppoe_timeout(sd_event_source *s, uint64_t usec, void *userdata) { | |
424 | sd_pppoe *ppp = userdata; | |
425 | int r; | |
426 | ||
427 | assert(ppp); | |
428 | ||
429 | switch (ppp->state) { | |
430 | case PPPOE_STATE_INITIALIZING: | |
431 | r = pppoe_send_initiation(ppp); | |
432 | if (r < 0) | |
433 | log_warning_errno(r, "PPPoE: sending PADI failed: %m"); | |
434 | ||
435 | break; | |
436 | case PPPOE_STATE_REQUESTING: | |
437 | if (ppp->padr_resend_count <= 0) { | |
438 | log_debug("PPPoE: PADR timed out, restarting PADI"); | |
439 | ||
440 | r = pppoe_send_initiation(ppp); | |
441 | if (r < 0) | |
442 | log_warning_errno(r, "PPPoE: sending PADI failed: %m"); | |
443 | ||
444 | ppp->padr_resend_count = PPPOE_MAX_PADR_RESEND; | |
445 | ppp->state = PPPOE_STATE_INITIALIZING; | |
446 | } else { | |
447 | r = pppoe_send_request(ppp); | |
448 | if (r < 0) | |
449 | log_warning_errno(r, "PPPoE: sending PADR failed: %m"); | |
450 | } | |
451 | ||
452 | break; | |
453 | default: | |
454 | assert_not_reached("timeout in invalid state"); | |
455 | } | |
456 | ||
457 | return 0; | |
458 | } | |
459 | ||
460 | static int pppoe_tag_parse_binary(struct pppoe_tag *tag, uint8_t **ret, size_t *length) { | |
461 | uint8_t *data; | |
462 | ||
463 | assert(ret); | |
464 | assert(length); | |
465 | ||
466 | data = memdup(tag->tag_data, PPPOE_TAG_LENGTH(tag)); | |
467 | if (!data) | |
468 | return -ENOMEM; | |
469 | ||
470 | free(*ret); | |
471 | *ret = data; | |
472 | *length = PPPOE_TAG_LENGTH(tag); | |
473 | ||
474 | return 0; | |
475 | } | |
476 | ||
477 | static int pppoe_tag_parse_string(struct pppoe_tag *tag, char **ret) { | |
478 | char *string; | |
479 | ||
480 | assert(ret); | |
481 | ||
482 | string = strndup(tag->tag_data, PPPOE_TAG_LENGTH(tag)); | |
483 | if (!string) | |
484 | return -ENOMEM; | |
485 | ||
486 | free(*ret); | |
487 | *ret = string; | |
488 | ||
489 | return 0; | |
490 | } | |
491 | ||
492 | static int pppoe_payload_parse(PPPoETags *tags, struct pppoe_hdr *header) { | |
493 | struct pppoe_tag *tag; | |
494 | int r; | |
495 | ||
496 | assert(tags); | |
497 | ||
498 | pppoe_tags_clear(tags); | |
499 | ||
500 | PPPOE_TAGS_FOREACH(tag, header) { | |
501 | switch (PPPOE_TAG_TYPE(tag)) { | |
502 | case PTT_SRV_NAME: | |
503 | r = pppoe_tag_parse_string(tag, &tags->service_name); | |
504 | if (r < 0) | |
505 | return r; | |
506 | ||
507 | break; | |
508 | case PTT_AC_NAME: | |
509 | r = pppoe_tag_parse_string(tag, &tags->ac_name); | |
510 | if (r < 0) | |
511 | return r; | |
512 | ||
513 | break; | |
514 | case PTT_HOST_UNIQ: | |
515 | r = pppoe_tag_parse_binary(tag, &tags->host_uniq, &tags->host_uniq_len); | |
516 | if (r < 0) | |
517 | return r; | |
518 | ||
519 | break; | |
520 | case PTT_AC_COOKIE: | |
521 | r = pppoe_tag_parse_binary(tag, &tags->cookie, &tags->cookie_len); | |
522 | if (r < 0) | |
523 | return r; | |
524 | ||
525 | break; | |
526 | case PTT_SRV_ERR: | |
527 | case PTT_SYS_ERR: | |
528 | case PTT_GEN_ERR: | |
529 | { | |
530 | _cleanup_free_ char *error = NULL; | |
531 | ||
532 | /* TODO: do something more sensible with the error messages */ | |
533 | r = pppoe_tag_parse_string(tag, &error); | |
534 | if (r < 0) | |
535 | return r; | |
536 | ||
537 | if (strlen(error) > 0 && utf8_is_valid(error)) | |
538 | log_debug("PPPoE: error - '%s'", error); | |
539 | else | |
540 | log_debug("PPPoE: error"); | |
541 | ||
542 | break; | |
543 | } | |
544 | default: | |
545 | log_debug("PPPoE: ignoring unknown PPPoE tag type: 0x%.2x", PPPOE_TAG_TYPE(tag)); | |
546 | } | |
547 | } | |
548 | ||
549 | return 0; | |
550 | } | |
551 | ||
552 | static int pppoe_open_pppoe_socket(sd_pppoe *ppp) { | |
553 | int s; | |
554 | ||
555 | assert(ppp); | |
556 | assert(ppp->pppoe_fd == -1); | |
557 | ||
558 | s = socket(AF_PPPOX, SOCK_STREAM, 0); | |
559 | if (s < 0) | |
560 | return -errno; | |
561 | ||
562 | ppp->pppoe_fd = s; | |
563 | ||
564 | return 0; | |
565 | } | |
566 | ||
567 | static int pppoe_connect_pppoe_socket(sd_pppoe *ppp) { | |
568 | union sockaddr_union_pppox link = { | |
569 | .pppox = { | |
570 | .sa_family = AF_PPPOX, | |
571 | .sa_protocol = PX_PROTO_OE, | |
572 | }, | |
573 | }; | |
574 | int r, channel; | |
575 | ||
576 | assert(ppp); | |
577 | assert(ppp->pppoe_fd != -1); | |
578 | assert(ppp->session_id); | |
579 | assert(ppp->ifname); | |
580 | ||
581 | link.pppox.sa_addr.pppoe.sid = ppp->session_id; | |
582 | memcpy(link.pppox.sa_addr.pppoe.dev, ppp->ifname, strlen(ppp->ifname)); | |
583 | memcpy(link.pppox.sa_addr.pppoe.remote, &ppp->peer_mac, ETH_ALEN); | |
584 | ||
585 | r = connect(ppp->pppoe_fd, &link.sa, sizeof(link.pppox)); | |
586 | if (r < 0) | |
587 | return r; | |
588 | ||
589 | r = ioctl(ppp->pppoe_fd, PPPIOCGCHAN, &channel); | |
590 | if (r < 0) | |
591 | return -errno; | |
592 | ||
593 | ppp->channel = channel; | |
594 | ||
595 | return 0; | |
596 | } | |
597 | ||
598 | static int pppoe_handle_message(sd_pppoe *ppp, struct pppoe_hdr *packet, struct ether_addr *mac) { | |
599 | int r; | |
600 | ||
601 | assert(packet); | |
602 | ||
603 | if (packet->ver != 0x1 || packet->type != 0x1) | |
604 | return 0; | |
605 | ||
606 | r = pppoe_payload_parse(&ppp->tags, packet); | |
607 | if (r < 0) | |
608 | return 0; | |
609 | ||
610 | switch (ppp->state) { | |
611 | case PPPOE_STATE_INITIALIZING: | |
612 | if (packet->code != PADO_CODE) | |
613 | return 0; | |
614 | ||
615 | if (ppp->tags.host_uniq_len != sizeof(ppp->host_uniq) || | |
616 | memcmp(ppp->tags.host_uniq, &ppp->host_uniq, sizeof(ppp->host_uniq)) != 0) | |
617 | return 0; | |
618 | ||
619 | log_debug("PPPoE: got OFFER (Peer: " | |
620 | "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx; " | |
621 | "Service-Name: '%s'; AC-Name: '%s')", | |
622 | mac->ether_addr_octet[0], | |
623 | mac->ether_addr_octet[1], | |
624 | mac->ether_addr_octet[2], | |
625 | mac->ether_addr_octet[3], | |
626 | mac->ether_addr_octet[4], | |
627 | mac->ether_addr_octet[5], | |
6300502b MP |
628 | strempty(ppp->tags.service_name), |
629 | strempty(ppp->tags.ac_name)); | |
f47781d8 MP |
630 | |
631 | memcpy(&ppp->peer_mac, mac, ETH_ALEN); | |
632 | ||
633 | r = pppoe_open_pppoe_socket(ppp); | |
634 | if (r < 0) { | |
635 | log_warning("PPPoE: could not open socket"); | |
636 | return r; | |
637 | } | |
638 | ||
639 | r = pppoe_send_request(ppp); | |
640 | if (r < 0) | |
641 | return 0; | |
642 | ||
643 | ppp->state = PPPOE_STATE_REQUESTING; | |
644 | ||
645 | break; | |
646 | case PPPOE_STATE_REQUESTING: | |
647 | if (packet->code != PADS_CODE) | |
648 | return 0; | |
649 | ||
650 | if (ppp->tags.host_uniq_len != sizeof(ppp->host_uniq) || | |
651 | memcmp(ppp->tags.host_uniq, &ppp->host_uniq, | |
652 | sizeof(ppp->host_uniq)) != 0) | |
653 | return 0; | |
654 | ||
655 | if (memcmp(&ppp->peer_mac, mac, ETH_ALEN) != 0) | |
656 | return 0; | |
657 | ||
658 | ppp->session_id = packet->sid; | |
659 | ||
660 | log_debug("PPPoE: got CONFIRMATION (Session ID: %"PRIu16")", | |
661 | be16toh(ppp->session_id)); | |
662 | ||
663 | r = pppoe_connect_pppoe_socket(ppp); | |
664 | if (r < 0) { | |
665 | log_warning("PPPoE: could not connect socket"); | |
666 | return r; | |
667 | } | |
668 | ||
669 | ppp->state = PPPOE_STATE_RUNNING; | |
670 | ||
671 | ppp->timeout = sd_event_source_unref(ppp->timeout); | |
672 | assert(ppp->cb); | |
6300502b | 673 | ppp->cb(ppp, SD_PPPOE_EVENT_RUNNING, ppp->userdata); |
f47781d8 MP |
674 | |
675 | break; | |
676 | case PPPOE_STATE_RUNNING: | |
677 | if (packet->code != PADT_CODE) | |
678 | return 0; | |
679 | ||
680 | if (memcmp(&ppp->peer_mac, mac, ETH_ALEN) != 0) | |
681 | return 0; | |
682 | ||
683 | if (ppp->session_id != packet->sid) | |
684 | return 0; | |
685 | ||
686 | log_debug("PPPoE: got TERMINATE"); | |
687 | ||
688 | ppp->state = PPPOE_STATE_STOPPED; | |
689 | ||
690 | assert(ppp->cb); | |
6300502b | 691 | ppp->cb(ppp, SD_PPPOE_EVENT_STOPPED, ppp->userdata); |
f47781d8 MP |
692 | |
693 | break; | |
694 | case PPPOE_STATE_STOPPED: | |
695 | break; | |
696 | default: | |
697 | assert_not_reached("PPPoE: invalid state when receiving message"); | |
698 | } | |
699 | ||
700 | return 0; | |
701 | } | |
702 | ||
703 | static int pppoe_receive_message(sd_event_source *s, int fd, uint32_t revents, void *userdata) { | |
704 | sd_pppoe *ppp = userdata; | |
705 | _cleanup_free_ struct pppoe_hdr *packet = NULL; | |
706 | union sockaddr_union link = {}; | |
707 | socklen_t addrlen = sizeof(link); | |
708 | int buflen = 0, len, r; | |
709 | ||
710 | assert(ppp); | |
711 | assert(fd != -1); | |
712 | ||
713 | r = ioctl(fd, FIONREAD, &buflen); | |
714 | if (r < 0) | |
715 | return r; | |
716 | ||
717 | if (buflen < 0) | |
718 | /* this can't be right */ | |
719 | return -EIO; | |
720 | ||
721 | packet = malloc0(buflen); | |
722 | if (!packet) | |
723 | return -ENOMEM; | |
724 | ||
725 | len = recvfrom(fd, packet, buflen, 0, &link.sa, &addrlen); | |
726 | if (len < 0) { | |
727 | log_warning_errno(r, "PPPoE: could not receive message from raw socket: %m"); | |
728 | return 0; | |
729 | } else if ((size_t)len < sizeof(struct pppoe_hdr)) | |
730 | return 0; | |
731 | else if ((size_t)len != sizeof(struct pppoe_hdr) + PPPOE_PACKET_LENGTH(packet)) | |
732 | return 0; | |
733 | ||
734 | if (link.ll.sll_halen != ETH_ALEN) | |
735 | /* not ethernet? */ | |
736 | return 0; | |
737 | ||
738 | r = pppoe_handle_message(ppp, packet, (struct ether_addr*)&link.ll.sll_addr); | |
739 | if (r < 0) | |
740 | return r; | |
741 | ||
742 | return 1; | |
743 | } | |
744 | ||
745 | int sd_pppoe_start(sd_pppoe *ppp) { | |
746 | union sockaddr_union link = { | |
747 | .ll = { | |
748 | .sll_family = AF_PACKET, | |
749 | .sll_protocol = htons(ETH_P_PPP_DISC), | |
750 | }, | |
751 | }; | |
752 | _cleanup_close_ int s = -1; | |
753 | _cleanup_event_source_unref_ sd_event_source *io = NULL; | |
754 | int r; | |
755 | ||
756 | assert_return(ppp, -EINVAL); | |
757 | assert_return(ppp->fd == -1, -EBUSY); | |
758 | assert_return(!ppp->io, -EBUSY); | |
759 | assert_return(ppp->ifindex > 0, -EUNATCH); | |
760 | assert_return(ppp->ifname, -EUNATCH); | |
761 | assert_return(ppp->event, -EUNATCH); | |
762 | assert_return(ppp->cb, -EUNATCH); | |
763 | ||
764 | s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); | |
765 | if (s < 0) | |
766 | return -errno; | |
767 | ||
768 | link.ll.sll_ifindex = ppp->ifindex; | |
769 | ||
770 | r = bind(s, &link.sa, sizeof(link.ll)); | |
771 | if (r < 0) | |
772 | return r; | |
773 | ||
774 | r = sd_event_add_io(ppp->event, &io, | |
775 | s, EPOLLIN, pppoe_receive_message, | |
776 | ppp); | |
777 | if (r < 0) | |
778 | return r; | |
779 | ||
780 | r = sd_event_source_set_priority(io, ppp->event_priority); | |
781 | if (r < 0) | |
782 | return r; | |
783 | ||
784 | ppp->fd = s; | |
785 | s = -1; | |
786 | ppp->io = io; | |
787 | io = NULL; | |
788 | ||
789 | r = pppoe_send_initiation(ppp); | |
790 | if (r < 0) | |
791 | return r; | |
792 | ||
793 | ppp->state = PPPOE_STATE_INITIALIZING; | |
794 | ||
795 | return 0; | |
796 | } | |
797 | ||
798 | int sd_pppoe_stop(sd_pppoe *ppp) { | |
799 | assert_return(ppp, -EINVAL); | |
800 | ||
801 | if (ppp->state == PPPOE_STATE_RUNNING) | |
802 | pppoe_send_terminate(ppp); | |
803 | ||
804 | ppp->io = sd_event_source_unref(ppp->io); | |
805 | ppp->timeout = sd_event_source_unref(ppp->timeout); | |
806 | ppp->fd = asynchronous_close(ppp->fd); | |
807 | ppp->pppoe_fd = asynchronous_close(ppp->pppoe_fd); | |
808 | ||
809 | return 0; | |
810 | } |