sd-pppoe.c revision a7f7d1bde43fc825c49afea3f946f5b4b3d563e0
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright (C) 2014 Tom Gundersen
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
/* See RFC 2516 */
#include <linux/ppp_defs.h>
#include <linux/ppp-ioctl.h>
#include <linux/if_pppox.h>
#include "sd-pppoe.h"
#include "event-util.h"
#include "util.h"
#include "socket-util.h"
#include "async.h"
#include "refcnt.h"
#include "utf8.h"
#define PPPOE_MAX_PACKET_SIZE 1484
#define PPPOE_MAX_PADR_RESEND 16
/* TODO: move this to socket-util.h without getting into
* a mess with the includes */
union sockaddr_union_pppox {
struct sockaddr_pppox pppox;
};
typedef enum PPPoEState {
_PPPOE_STATE_INVALID = -1,
} PPPoEState;
typedef struct PPPoETags {
char *service_name;
char *ac_name;
} PPPoETags;
struct sd_pppoe {
int ifindex;
char *ifname;
int event_priority;
int fd;
int padr_resend_count;
char *service_name;
struct ether_addr peer_mac;
int pppoe_fd;
int channel;
void *userdata;
};
#define PPPOE_PACKET_LENGTH(header) \
#define PPPOE_PACKET_TAIL(packet) \
#define PPPOE_TAG_LENGTH(tag) \
#define PPPOE_TAG_TYPE(tag) \
#define PPPOE_TAG_NEXT(tag) \
}
return 0;
}
char *name;
return -EINVAL;
if (!name)
return -ENOMEM;
return 0;
}
if (service_name) {
if (!name)
return -ENOMEM;
}
return 0;
}
int r;
if (event)
else {
if (r < 0)
return r;
}
return 0;
}
return 0;
}
if (ppp)
return ppp;
}
}
return NULL;
}
if (!ppp)
return -ENOMEM;
return 0;
}
return 0;
}
return 0;
}
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) {
assert(sizeof(struct pppoe_hdr) + PPPOE_PACKET_LENGTH(packet) + sizeof(struct pppoe_tag) + tag_len <= packet_size);
if (tag_data)
}
union sockaddr_union link = {
.ll = {
.sll_family = AF_PACKET,
},
};
int r;
else
if (!packet)
return -ENOMEM;
/* Service-Name */
/* AC-Cookie */
/* Host-Uniq */
}
if (r < 0)
return -errno;
return 0;
}
usec_t next_timeout = 0;
int r;
if (r == -ENODATA)
else if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
return 0;
}
int r;
if (r < 0)
return r;
log_debug("PPPoE: sent DISCOVER (Service-Name: %s)",
return r;
}
int r;
if (r < 0)
return r;
log_debug("PPPoE: sent REQUEST");
ppp->padr_resend_count --;
return 0;
}
int r;
if (r < 0)
return r;
log_debug("PPPoE: sent TERMINATE");
return 0;
}
int r;
case PPPOE_STATE_INITIALIZING:
r = pppoe_send_initiation(ppp);
if (r < 0)
log_warning_errno(r, "PPPoE: sending PADI failed: %m");
break;
case PPPOE_STATE_REQUESTING:
if (ppp->padr_resend_count <= 0) {
log_debug("PPPoE: PADR timed out, restarting PADI");
r = pppoe_send_initiation(ppp);
if (r < 0)
log_warning_errno(r, "PPPoE: sending PADI failed: %m");
} else {
r = pppoe_send_request(ppp);
if (r < 0)
log_warning_errno(r, "PPPoE: sending PADR failed: %m");
}
break;
default:
assert_not_reached("timeout in invalid state");
}
return 0;
}
if (!data)
return -ENOMEM;
return 0;
}
char *string;
if (!string)
return -ENOMEM;
return 0;
}
int r;
switch (PPPOE_TAG_TYPE(tag)) {
case PTT_SRV_NAME:
if (r < 0)
return r;
break;
case PTT_AC_NAME:
if (r < 0)
return r;
break;
case PTT_HOST_UNIQ:
if (r < 0)
return r;
break;
case PTT_AC_COOKIE:
if (r < 0)
return r;
break;
case PTT_SRV_ERR:
case PTT_SYS_ERR:
case PTT_GEN_ERR:
{
/* TODO: do something more sensible with the error messages */
if (r < 0)
return r;
else
log_debug("PPPoE: error");
break;
}
default:
}
}
return 0;
}
int s;
if (s < 0)
return -errno;
return 0;
}
union sockaddr_union_pppox link = {
.pppox = {
},
};
int r, channel;
if (r < 0)
return r;
if (r < 0)
return -errno;
return 0;
}
int r;
return 0;
if (r < 0)
return 0;
case PPPOE_STATE_INITIALIZING:
return 0;
return 0;
log_debug("PPPoE: got OFFER (Peer: "
"%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx; "
"Service-Name: '%s'; AC-Name: '%s')",
mac->ether_addr_octet[0],
r = pppoe_open_pppoe_socket(ppp);
if (r < 0) {
log_warning("PPPoE: could not open socket");
return r;
}
r = pppoe_send_request(ppp);
if (r < 0)
return 0;
break;
case PPPOE_STATE_REQUESTING:
return 0;
return 0;
return 0;
if (r < 0) {
log_warning("PPPoE: could not connect socket");
return r;
}
break;
case PPPOE_STATE_RUNNING:
return 0;
return 0;
return 0;
log_debug("PPPoE: got TERMINATE");
break;
case PPPOE_STATE_STOPPED:
break;
default:
assert_not_reached("PPPoE: invalid state when receiving message");
}
return 0;
}
union sockaddr_union link = {};
if (r < 0)
return r;
if (buflen < 0)
/* this can't be right */
return -EIO;
if (!packet)
return -ENOMEM;
if (len < 0) {
log_warning_errno(r, "PPPoE: could not receive message from raw socket: %m");
return 0;
return 0;
return 0;
/* not ethernet? */
return 0;
if (r < 0)
return r;
return 1;
}
union sockaddr_union link = {
.ll = {
.sll_family = AF_PACKET,
},
};
_cleanup_close_ int s = -1;
int r;
if (s < 0)
return -errno;
if (r < 0)
return r;
ppp);
if (r < 0)
return r;
if (r < 0)
return r;
s = -1;
r = pppoe_send_initiation(ppp);
if (r < 0)
return r;
return 0;
}
return 0;
}