Add src
This commit is contained in:
19
Makefile
Normal file
19
Makefile
Normal file
@@ -0,0 +1,19 @@
|
||||
CC ?= cc
|
||||
CFLAGS += -fPIC -Wall -Wextra
|
||||
LDFLAGS += -shared -ljansson
|
||||
LIBNAME = netscan
|
||||
TARGET = lib$(LIBNAME).so
|
||||
|
||||
SOURCES = $(wildcard ./src/*.c)
|
||||
OBJS = $(patsubst %.c,%.o,$(SOURCES))
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJS)
|
||||
$(LD) $(LDFLAGS) -o $@ $(OBJS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET) $(OBJS)
|
||||
18
include/netscan.h
Normal file
18
include/netscan.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Konstantin Vasin
|
||||
*
|
||||
* Licensed under GPLv2, see file LICENSE for more information.
|
||||
*/
|
||||
#ifndef _NETSCAN_H_
|
||||
#define _NETSCAN_H_
|
||||
|
||||
#include <jansson.h>
|
||||
|
||||
extern int scan_dhcp(const char *ifname, unsigned int time, json_t *result);
|
||||
extern int scan_pppoe(const char *ifname, unsigned int time, json_t *result);
|
||||
|
||||
/* return only discovery result without additional info */
|
||||
extern int scan_pppoe_result(const char *ifname, unsigned int time);
|
||||
extern int scan_dhcp_result(const char *ifname, unsigned int time);
|
||||
|
||||
#endif /* _NETSCAN_H_ */
|
||||
166
src/scan_common.c
Normal file
166
src/scan_common.c
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Konstantin Vasin
|
||||
*
|
||||
* Licensed under GPLv2, see file LICENSE for more information.
|
||||
*/
|
||||
#include "scan_common.h"
|
||||
|
||||
/* Endianness check */
|
||||
#define IS_BIG_ENDIAN (*(uint16_t *)"\0\xff" < 0x100)
|
||||
|
||||
/**
|
||||
* Get MAC address of interface.
|
||||
*
|
||||
* @param ifname Interface name.
|
||||
* @param addr Array for MAC address.
|
||||
*
|
||||
* @return -1 on failure, 0 on success.
|
||||
*/
|
||||
int s_get_hw_address(const char *ifname, uint8_t addr[ETH_ALEN])
|
||||
{
|
||||
/* Copy interface name into ifreq */
|
||||
struct ifreq ifr;
|
||||
memset(&ifr, 0, sizeof(struct ifreq));
|
||||
|
||||
size_t if_name_len = strlen(ifname);
|
||||
|
||||
if (if_name_len < sizeof(ifr.ifr_name)) {
|
||||
memcpy(ifr.ifr_name, ifname, if_name_len);
|
||||
ifr.ifr_name[if_name_len] = 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Create socket */
|
||||
int fd = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
|
||||
if (fd < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get hw address */
|
||||
if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
|
||||
close (fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
memcpy (addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get Interface index.
|
||||
*
|
||||
* @param ifname Interface name.
|
||||
*
|
||||
* @return Interface index on success, zero on failure.
|
||||
*/
|
||||
unsigned int s_get_ifindex(const char *ifname)
|
||||
{
|
||||
/* Copy interface name into ifreq */
|
||||
struct ifreq ifr;
|
||||
memset(&ifr, 0, sizeof(struct ifreq));
|
||||
|
||||
size_t if_name_len = strlen(ifname);
|
||||
|
||||
if (if_name_len < sizeof(ifr.ifr_name)) {
|
||||
memcpy(ifr.ifr_name, ifname, if_name_len);
|
||||
ifr.ifr_name[if_name_len] = 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Create socket */
|
||||
int fd = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
|
||||
if (fd < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get interface index */
|
||||
if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
|
||||
close (fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return ifr.ifr_ifindex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate internet checksum.
|
||||
*
|
||||
* @param addr Start address.
|
||||
* @param nleft Length in bytes.
|
||||
*
|
||||
* @return Checksum.
|
||||
*/
|
||||
uint16_t s_inet_cksum(uint16_t *addr, int nleft)
|
||||
{
|
||||
/*
|
||||
* Algorithm is simple, using a 32 bit accumulator,
|
||||
* we add sequential 16 bit words to it, and at the end, fold
|
||||
* back all the carry bits from the top 16 bits into the lower
|
||||
* 16 bits.
|
||||
*/
|
||||
unsigned sum = 0;
|
||||
while (nleft > 1) {
|
||||
sum += *addr++;
|
||||
nleft -= 2;
|
||||
}
|
||||
/* Mop up an odd byte, if necessary */
|
||||
if (nleft == 1) {
|
||||
if (IS_BIG_ENDIAN)
|
||||
sum += *(uint8_t *)addr << 8;
|
||||
else
|
||||
sum += *(uint8_t *)addr;
|
||||
}
|
||||
|
||||
/* Add back carry outs from top 16 bits to low 16 bits */
|
||||
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
|
||||
sum += (sum >> 16); /* add carry */
|
||||
|
||||
return (uint16_t)~sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate random id (unsigned 32-bit value).
|
||||
*
|
||||
* @param Void.
|
||||
* @return id
|
||||
*/
|
||||
uint32_t s_random_id(void)
|
||||
{
|
||||
srand(time(NULL));
|
||||
return rand();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add error value in result.
|
||||
*
|
||||
* @param JSON object.
|
||||
* @param msg Error message
|
||||
*
|
||||
* @return 0 on success, -1 on failure.
|
||||
*/
|
||||
int s_set_err(json_t *result, const char *msg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char buf[MAX_ERROR_LENGTH];
|
||||
int ret = -1;
|
||||
|
||||
|
||||
va_start(ap, msg);
|
||||
if (msg != NULL)
|
||||
ret = vsnprintf(buf, sizeof(buf), msg, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (ret > 0)
|
||||
ret = json_object_set_new_nocheck(result, "error", json_string(buf));
|
||||
else
|
||||
ret = -1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
35
src/scan_common.h
Normal file
35
src/scan_common.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Konstantin Vasin
|
||||
*
|
||||
* Licensed under GPLv2, see file LICENSE for more information.
|
||||
*/
|
||||
#ifndef _SCAN_COMMON_H
|
||||
#define _SCAN_COMMON_H
|
||||
|
||||
#include <jansson.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netpacket/packet.h>
|
||||
#include <netinet/if_ether.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/udp.h>
|
||||
#include <linux/if.h>
|
||||
|
||||
#define MAX_WAIT_TIME 15
|
||||
#define MAX_ERROR_LENGTH 1500
|
||||
|
||||
extern uint32_t s_random_id(void);
|
||||
extern int s_get_hw_address(const char *ifname, uint8_t addr[ETH_ALEN]);
|
||||
extern unsigned int s_get_ifindex(const char *ifname);
|
||||
extern uint16_t s_inet_cksum(uint16_t *addr, int nleft);
|
||||
extern int s_set_err(json_t *result, const char *msg, ...);
|
||||
|
||||
#endif /* _SCAN_COMMON_H */
|
||||
619
src/scan_dhcp.c
Normal file
619
src/scan_dhcp.c
Normal file
@@ -0,0 +1,619 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Konstantin Vasin
|
||||
*
|
||||
* Licensed under GPLv2, see file LICENSE for more information.
|
||||
*/
|
||||
#include "scan_common.h"
|
||||
|
||||
/* DHCP protocol. RFC 2131, RFC 2132 */
|
||||
#define DHCP_MAGIC 0x63825363
|
||||
#define DHCP_FIXED_LEN 240 /* with DHCP magic */
|
||||
#define DHCP_UDP_OVERHEAD 28 /* IP + UDP headers */
|
||||
#define DHCP_MTU_MAX 1500
|
||||
#define DHCP_MTU_MIN 576
|
||||
#define DHCP_OPTIONS_BUF_MIN (DHCP_MTU_MIN - DHCP_FIXED_LEN - DHCP_UDP_OVERHEAD)
|
||||
#define DHCP_OPTIONS_BUF_MAX (DHCP_MTU_MAX - DHCP_FIXED_LEN - DHCP_UDP_OVERHEAD)
|
||||
|
||||
#define BOOTREQUEST 1
|
||||
#define BOOTREPLY 2
|
||||
|
||||
/* DHCP Ports and Addresses */
|
||||
#define CLIENT_PORT 68
|
||||
#define SERVER_PORT 67
|
||||
|
||||
/* DHCP packet */
|
||||
struct dhcp_packet {
|
||||
uint8_t op; /* BOOTREQUEST or BOOTREPLY */
|
||||
uint8_t htype; /* hardware address type. 1 = 10Mb ethernet */
|
||||
uint8_t hlen; /* hardware address length */
|
||||
uint8_t hops; /* used by relay agents only */
|
||||
uint32_t xid; /* unuque id */
|
||||
uint16_t secs; /* seconds since client started looking */
|
||||
uint16_t flags; /* only one flag */
|
||||
uint32_t ciaddr; /* clients IP address (if already in use) */
|
||||
uint32_t yiaddr; /* client IP address */
|
||||
uint32_t siaddr_nip; /* next server used in bootstrap */
|
||||
uint32_t gateway_nip; /* relay agent IP address */
|
||||
uint8_t chaddr[16]; /* MAC address of client */
|
||||
uint8_t sname[64]; /* server host name (ASCIZ) */
|
||||
uint8_t file[128]; /* boot file name (ASCIIZ) */
|
||||
uint32_t cookie; /* fixed first four option bytes (99, 130, 83, 99 dec) */
|
||||
uint8_t options[DHCP_OPTIONS_BUF_MAX];
|
||||
};
|
||||
|
||||
/* IP packet with DHCP */
|
||||
struct ip_udp_dhcp_packet {
|
||||
struct iphdr ip; /* IP header */
|
||||
struct udphdr udp; /* UDP header */
|
||||
struct dhcp_packet data; /* UDP payload */
|
||||
};
|
||||
|
||||
/* UDP packet with DHCP */
|
||||
struct udp_dhcp_packet {
|
||||
struct udphdr udp; /* UDP header */
|
||||
struct dhcp_packet data; /* UDP payload */
|
||||
};
|
||||
|
||||
/* Packets size */
|
||||
enum {
|
||||
IP_UDP_DHCP_SIZE = sizeof(struct ip_udp_dhcp_packet),
|
||||
UDP_DHCP_SIZE = sizeof(struct udp_dhcp_packet),
|
||||
DHCP_SIZE = sizeof(struct dhcp_packet),
|
||||
};
|
||||
|
||||
/* DHCP options codes */
|
||||
#define DHCP_PADDING 0x00
|
||||
#define DHCP_SUBNET 0x01
|
||||
#define DHCP_ROUTER 0x03
|
||||
#define DHCP_DNS 0x06
|
||||
#define DHCP_BROADCAST 0x1c
|
||||
#define DHCP_STATIC 0x21
|
||||
#define DHCP_NTP 0x2a
|
||||
#define DHCP_VENDOR 0x2b
|
||||
#define DHCP_NETBIOS_NAME_SRV 0x2c
|
||||
#define DHCP_LEASE_TIME 0x33
|
||||
#define DHCP_OVERLOAD 0x34
|
||||
#define DHCP_MESSAGE_TYPE 0x35
|
||||
#define DHCP_SERVER_ID 0x36 /* DHCP server IP */
|
||||
#define DHCP_PARAM_REQ 0x37 /* list of options client wants */
|
||||
#define DHCP_MAX_SIZE 0x39
|
||||
#define DHCP_RENEWAL_TIME 0x3a
|
||||
#define DHCP_REBINDING_TIME 0x3b
|
||||
#define DHCP_VENDOR_CLASS_ID 0x3c
|
||||
#define DHCP_CLIENT_ID 0x3d /* client's MAC addr*/
|
||||
#define DHCP_SIP 0x78
|
||||
#define DHCP_CLASSLESS_STATIC 0x79
|
||||
#define DHCP_END 0xff
|
||||
|
||||
/* DHCP_MESSAGE_TYPE values */
|
||||
#define DHCPDISCOVER 1 /* client -> server */
|
||||
#define DHCPOFFER 2 /* client <- server */
|
||||
|
||||
/* Offsets in option byte sequence */
|
||||
#define OPT_CODE 0
|
||||
#define OPT_LEN 1
|
||||
#define OPT_DATA 2
|
||||
|
||||
/* Bits in "overload" option */
|
||||
#define OPTION_FIELD 0
|
||||
#define FILE_FIELD 1
|
||||
#define SNAME_FIELD 2
|
||||
|
||||
static int dhcp_send_discover(int fd, int ifindex, uint8_t *hwaddr, uint32_t id);
|
||||
static void dhcp_init_packet(struct dhcp_packet *packet, uint8_t *hwaddr);
|
||||
static int dhcp_end_option(uint8_t *optionptr);
|
||||
static int dhcp_recv_offer(int fd, uint32_t id, uint8_t *hwaddr, json_t *result);
|
||||
static uint8_t *dhcp_get_option(struct dhcp_packet *packet, int code);
|
||||
static void handler(int sig);
|
||||
|
||||
static volatile sig_atomic_t got_alarm = 0;
|
||||
|
||||
/**
|
||||
* Search DHCP server.
|
||||
*
|
||||
* @param ifname Network interface name.
|
||||
* @param time Wait time.
|
||||
* @param result JSON object with result.
|
||||
*
|
||||
* @return 1 on success, 0 on time is out, -2 on invalid json object for result,
|
||||
* -1 on other errors.
|
||||
*/
|
||||
int scan_dhcp(const char *ifname, unsigned int time, json_t *result)
|
||||
{
|
||||
int ifindex;
|
||||
uint8_t hwaddr[ETH_ALEN];
|
||||
struct sigaction sa;
|
||||
uid_t euid;
|
||||
uint32_t id;
|
||||
int fd = -1;
|
||||
int ret = -1;
|
||||
|
||||
/* Check pointer for result */
|
||||
if (!json_is_object(result)) {
|
||||
ret = - 2;
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
/* Check superuser priveleges */
|
||||
if ((euid = geteuid()) != 0) {
|
||||
s_set_err(result, "You must be root");
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
/* Check input data */
|
||||
/* Check wait time */
|
||||
if (time == 0)
|
||||
time = 1;
|
||||
if (time > MAX_WAIT_TIME) {
|
||||
s_set_err(result, "Max wait time is %d", MAX_WAIT_TIME);
|
||||
goto err_exit;
|
||||
}
|
||||
/* Check interface name */
|
||||
if (!ifname || *ifname == '\0') {
|
||||
s_set_err(result, "Empty interface name");
|
||||
goto err_exit;
|
||||
}
|
||||
/* Check interface name length */
|
||||
if (strlen(ifname) > IFNAMSIZ) {
|
||||
s_set_err(result, "Interface name is too long");
|
||||
goto err_exit;
|
||||
}
|
||||
/* Get interface index */
|
||||
if ((ifindex = s_get_ifindex(ifname)) == 0) {
|
||||
s_set_err(result, "No interface found");
|
||||
goto err_exit;
|
||||
}
|
||||
/* Get MAC address */
|
||||
if ((s_get_hw_address(ifname, hwaddr)) < 0) {
|
||||
s_set_err(result, "Can't get MAC address");
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
/* Create socket */
|
||||
fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
|
||||
|
||||
if (fd < 0) {
|
||||
s_set_err(result, "Can't create socket");
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
/* Establish handler for notification signal */
|
||||
memset(&sa, 0, sizeof(struct sigaction));
|
||||
sa.sa_flags = 0;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_handler = handler;
|
||||
if ((sigaction(SIGALRM, &sa, NULL)) == -1) {
|
||||
s_set_err(result, "Can't set handler for timer");
|
||||
goto err_exit2;
|
||||
}
|
||||
|
||||
/* Send Request */
|
||||
id = s_random_id();
|
||||
ret = dhcp_send_discover(fd, ifindex, hwaddr, id);
|
||||
if (ret < 0) {
|
||||
s_set_err(result, "Can't send request");
|
||||
goto err_exit2;
|
||||
}
|
||||
|
||||
json_object_clear(result);
|
||||
|
||||
/* Start timer */
|
||||
alarm(time);
|
||||
|
||||
/* Loop, recv packets && search answer for us */
|
||||
while(1) {
|
||||
ret = dhcp_recv_offer(fd, id, hwaddr, result);
|
||||
|
||||
/* Answer received */
|
||||
if (ret == 0) {
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
/* Read error */
|
||||
if (ret == -1) {
|
||||
s_set_err(result, "Read socket error");
|
||||
break;
|
||||
}
|
||||
/* Check timer */
|
||||
if (got_alarm == 1) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Cancel timer */
|
||||
alarm(0);
|
||||
err_exit2:
|
||||
close(fd);
|
||||
err_exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search DHCP server.
|
||||
*
|
||||
* @param ifname Network interface name.
|
||||
* @param time Wait time.
|
||||
*
|
||||
* @return 1 on success, 0 on time is out, -1 on error.
|
||||
*/
|
||||
int scan_dhcp_result(const char *ifname, unsigned int time) {
|
||||
|
||||
int ret;
|
||||
json_t *result = json_object();
|
||||
|
||||
ret = scan_dhcp(ifname, time, result);
|
||||
if (ret < 0)
|
||||
ret = -1;
|
||||
|
||||
json_decref(result);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send DHCP DISCOVER
|
||||
*
|
||||
* @param fd Socket descriptor.
|
||||
* @param ifindex Interface index.
|
||||
* @param hwaddr MAC address of interface.
|
||||
* @param id Unique ID.
|
||||
*
|
||||
* @return Number of bytes sent on success, -1 on error.
|
||||
*/
|
||||
static int dhcp_send_discover(int fd, int ifindex, uint8_t *hwaddr, uint32_t id)
|
||||
{
|
||||
struct sockaddr_ll sa;
|
||||
struct ip_udp_dhcp_packet packet;
|
||||
unsigned padding;
|
||||
int result = -1;
|
||||
|
||||
/* Bind */
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sll_family = AF_PACKET;
|
||||
sa.sll_protocol = htons(ETH_P_IP);
|
||||
sa.sll_ifindex = ifindex;
|
||||
sa.sll_halen = ETH_ALEN;
|
||||
memset(&sa.sll_addr, 0xff, ETH_ALEN);
|
||||
|
||||
if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Craft packet */
|
||||
memset(&packet, 0, sizeof(packet));
|
||||
dhcp_init_packet(&packet.data, hwaddr);
|
||||
packet.data.xid = id;
|
||||
|
||||
/* For badly configured servers (they drop DHCP packets > 576 octets (with ethernet header),
|
||||
* but they may only drop packets > 576 octets without ethernet header (590 with ethernet header).
|
||||
* RFC 1542: minimal BOOTP header 300 octets.
|
||||
*/
|
||||
padding = DHCP_OPTIONS_BUF_MAX - 1 - dhcp_end_option(packet.data.options);
|
||||
if (padding > DHCP_SIZE - 300)
|
||||
padding = DHCP_SIZE - 300;
|
||||
|
||||
/* IP and UDP headers */
|
||||
packet.ip.protocol = IPPROTO_UDP;
|
||||
packet.ip.saddr = htonl(INADDR_ANY);
|
||||
packet.ip.daddr = htonl(INADDR_BROADCAST);
|
||||
packet.udp.source = htons(CLIENT_PORT);
|
||||
packet.udp.dest = htons(SERVER_PORT);
|
||||
/* size, excluding IP header */
|
||||
packet.udp.len = htons(UDP_DHCP_SIZE - padding);
|
||||
/* for UDP checksumming, ip.len is set to UDP packet len */
|
||||
packet.ip.tot_len = packet.udp.len;
|
||||
|
||||
packet.udp.check = s_inet_cksum((uint16_t *)&packet,
|
||||
IP_UDP_DHCP_SIZE - padding);
|
||||
/* for sending, it is set to IP packet len */
|
||||
packet.ip.tot_len = htons(IP_UDP_DHCP_SIZE - padding);
|
||||
packet.ip.version = IPVERSION;
|
||||
packet.ip.ihl = sizeof(packet.ip) >> 2;
|
||||
packet.ip.ttl = IPDEFTTL;
|
||||
packet.ip.check = s_inet_cksum((uint16_t *)&packet.ip, sizeof(packet.ip));
|
||||
|
||||
/* Send packet */
|
||||
result = sendto(fd, &packet, IP_UDP_DHCP_SIZE - padding, 0,
|
||||
(struct sockaddr *)&sa, sizeof(sa));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill DHCP Packet.
|
||||
*
|
||||
* @param packet DHCP packet.
|
||||
* @param hwaddr MAC address of interface.
|
||||
* @return Void.
|
||||
*/
|
||||
static void dhcp_init_packet(struct dhcp_packet *packet, uint8_t *hwaddr)
|
||||
{
|
||||
unsigned index = 0;
|
||||
|
||||
/* DHCP header */
|
||||
memset(packet, 0, sizeof(*packet));
|
||||
packet->op = BOOTREQUEST;
|
||||
packet->htype = 1; /* ethernet */
|
||||
packet->hlen = ETH_ALEN;
|
||||
packet->cookie = htonl(DHCP_MAGIC);
|
||||
packet->secs = 0;
|
||||
memcpy(packet->chaddr, hwaddr, ETH_ALEN);
|
||||
|
||||
/* DHCP options */
|
||||
/* Message type */
|
||||
packet->options[index++] = DHCP_MESSAGE_TYPE;
|
||||
packet->options[index++] = 1; //length of option data
|
||||
packet->options[index++] = DHCPDISCOVER;
|
||||
|
||||
/* Requested parameters */
|
||||
packet->options[index++] = DHCP_PARAM_REQ;
|
||||
packet->options[index++] = 9; // number of options
|
||||
packet->options[index++] = DHCP_SUBNET;
|
||||
packet->options[index++] = DHCP_ROUTER;
|
||||
packet->options[index++] = DHCP_DNS;
|
||||
packet->options[index++] = DHCP_BROADCAST;
|
||||
packet->options[index++] = DHCP_STATIC;
|
||||
packet->options[index++] = DHCP_NTP;
|
||||
packet->options[index++] = DHCP_VENDOR;
|
||||
packet->options[index++] = DHCP_SIP;
|
||||
packet->options[index++] = DHCP_CLASSLESS_STATIC;
|
||||
|
||||
/* client id */
|
||||
packet->options[index++] = DHCP_CLIENT_ID;
|
||||
packet->options[index++] = ETH_ALEN + 1;
|
||||
packet->options[index++] = 1; //ethernet
|
||||
memcpy(&packet->options[index], hwaddr, ETH_ALEN);
|
||||
index += ETH_ALEN;
|
||||
|
||||
/* end option */
|
||||
packet->options[index] = DHCP_END;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate position of the END option.
|
||||
*
|
||||
* @param optionptr Beginning of dhcp options.
|
||||
* @return Position of 'end' option.
|
||||
*/
|
||||
static int dhcp_end_option(uint8_t *optionptr)
|
||||
{
|
||||
int i = 0;
|
||||
while (optionptr[i] != DHCP_END) {
|
||||
if (optionptr[i] != DHCP_PADDING)
|
||||
i += optionptr[i + OPT_LEN] + OPT_DATA - 1;
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
* Receive OFFER
|
||||
*
|
||||
* @param fd Socket descriptor.
|
||||
* @param id Unique ID.
|
||||
* @param hwaddr MAC address of interface.
|
||||
* @param result JSON object for result.
|
||||
*
|
||||
* @return 0 on success, -1 on read error,
|
||||
* -2 packet is not correct, -3 on EINTR (time is out).
|
||||
*/
|
||||
static int dhcp_recv_offer(int fd, uint32_t id, uint8_t *hwaddr, json_t *result)
|
||||
{
|
||||
int bytes;
|
||||
struct ip_udp_dhcp_packet packet;
|
||||
struct dhcp_packet data;
|
||||
|
||||
uint8_t *opt_data;
|
||||
uint16_t check;
|
||||
char ip_str[INET_ADDRSTRLEN];
|
||||
uint32_t ip_addr;
|
||||
int i;
|
||||
|
||||
memset(&packet, 0, sizeof(packet));
|
||||
memset(&data, 0, sizeof(data));
|
||||
|
||||
/* Read packet */
|
||||
bytes = read(fd, &packet, sizeof(packet));
|
||||
|
||||
if (bytes < 0) {
|
||||
if (errno == EINTR) {
|
||||
return -3;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/* Packet is too short */
|
||||
if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp)))
|
||||
return -2;
|
||||
/* Oversized packet */
|
||||
if (bytes < (int) ntohs(packet.ip.tot_len))
|
||||
return -2;
|
||||
|
||||
/* Ignore any extra garbage bytes */
|
||||
bytes = ntohs(packet.ip.tot_len);
|
||||
|
||||
/* Unrelated/bogus packet */
|
||||
if (packet.ip.protocol != IPPROTO_UDP
|
||||
|| packet.ip.version != IPVERSION
|
||||
|| packet.ip.ihl != (sizeof(packet.ip) >> 2)
|
||||
|| packet.udp.dest != htons(CLIENT_PORT)
|
||||
|| ntohs(packet.udp.len) != (uint16_t)(bytes - sizeof(packet.ip))
|
||||
)
|
||||
return -2;
|
||||
|
||||
/* Verify IP checksum */
|
||||
check = packet.ip.check;
|
||||
packet.ip.check = 0;
|
||||
if (check != s_inet_cksum((uint16_t *)&packet.ip, sizeof(packet.ip)))
|
||||
return -2;
|
||||
|
||||
/* Verify UDP checksum, IP header has to be modified for this */
|
||||
memset(&packet.ip, 0, offsetof(struct iphdr, protocol));
|
||||
/* ip.xx fields which are not memset: protocol, check, saddr, daddr */
|
||||
packet.ip.tot_len = packet.udp.len;
|
||||
check = packet.udp.check;
|
||||
packet.udp.check = 0;
|
||||
if (check && check != s_inet_cksum((uint16_t *)&packet, bytes))
|
||||
return -2;
|
||||
|
||||
memcpy(&data, &packet.data, sizeof(data));
|
||||
|
||||
/* Check DHCP magic */
|
||||
if (bytes < (int)offsetof(struct dhcp_packet, options)
|
||||
|| data.cookie != htonl(DHCP_MAGIC))
|
||||
return -2;
|
||||
|
||||
/* Check xid */
|
||||
if (data.xid != id)
|
||||
return -2;
|
||||
|
||||
/* Ignore packets that aren't for us */
|
||||
if (data.hlen != ETH_ALEN || memcmp(&data.chaddr, hwaddr, ETH_ALEN))
|
||||
return -2;
|
||||
|
||||
/* Check message type */
|
||||
opt_data = dhcp_get_option(&data, DHCP_MESSAGE_TYPE);
|
||||
if (!opt_data || *opt_data != DHCPOFFER)
|
||||
return -2;
|
||||
|
||||
/* Fill result */
|
||||
/* Get DHCP server ID */
|
||||
opt_data = dhcp_get_option(&data, DHCP_SERVER_ID);
|
||||
if (opt_data) {
|
||||
ip_addr = *(uint32_t *)opt_data;
|
||||
if (inet_ntop(AF_INET, &ip_addr, ip_str, sizeof(ip_str)))
|
||||
json_object_set_new_nocheck(result, "server", json_string(ip_str));
|
||||
}
|
||||
|
||||
/* Get netmask */
|
||||
opt_data = dhcp_get_option(&data, DHCP_SUBNET);
|
||||
if (opt_data) {
|
||||
ip_addr = *(uint32_t *)opt_data;
|
||||
if (inet_ntop(AF_INET, &ip_addr, ip_str, sizeof(ip_str)))
|
||||
json_object_set_new_nocheck(result, "mask", json_string(ip_str));
|
||||
}
|
||||
|
||||
/* Get router */
|
||||
opt_data = dhcp_get_option(&data, DHCP_ROUTER);
|
||||
if (opt_data) {
|
||||
ip_addr = *(uint32_t *)opt_data;
|
||||
if (inet_ntop(AF_INET, &ip_addr, ip_str, sizeof(ip_str)))
|
||||
json_object_set_new_nocheck(result, "router", json_string(ip_str));
|
||||
}
|
||||
/* Get DNS servers */
|
||||
opt_data = dhcp_get_option(&data, DHCP_DNS);
|
||||
if (opt_data) {
|
||||
json_t *dns_list = json_array();
|
||||
int num = *(opt_data - 1) >> 2; /* get number of servers */
|
||||
for (i = 0; i < num; i++) {
|
||||
ip_addr = (*((uint32_t *)opt_data + i));
|
||||
if (inet_ntop(AF_INET, &ip_addr, ip_str, sizeof(ip_str)))
|
||||
json_array_append_new(dns_list, json_string(ip_str));
|
||||
else
|
||||
break;
|
||||
}
|
||||
json_object_set_new_nocheck(result, "dns", dns_list);
|
||||
}
|
||||
/* Get Broadcast address */
|
||||
opt_data = dhcp_get_option(&data, DHCP_BROADCAST);
|
||||
if (opt_data) {
|
||||
ip_addr = *(uint32_t *)opt_data;
|
||||
if (inet_ntop(AF_INET, &ip_addr, ip_str, sizeof(ip_str)))
|
||||
json_object_set_new_nocheck(result, "broadcast", json_string(ip_str));
|
||||
}
|
||||
/* Get NETBIOS Name servers */
|
||||
opt_data = dhcp_get_option(&data, DHCP_NETBIOS_NAME_SRV);
|
||||
if (opt_data) {
|
||||
json_t *netbios_list = json_array();
|
||||
int num = *(opt_data - 1) >> 2; /* get number of servers */
|
||||
for (i = 0; i < num; i++) {
|
||||
ip_addr = *((uint32_t *)opt_data + i);
|
||||
if (inet_ntop(AF_INET, &ip_addr, ip_str, sizeof(ip_str)))
|
||||
json_array_append_new(netbios_list, json_string(ip_str));
|
||||
else
|
||||
break;
|
||||
}
|
||||
json_object_set_new_nocheck(result, "netbios", netbios_list);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an option with bounds checking
|
||||
*
|
||||
* @param packet DHCP packet.
|
||||
* @param code Option code.
|
||||
*
|
||||
* @return NULL or pointer to beginning of the option data.
|
||||
*/
|
||||
static uint8_t *dhcp_get_option(struct dhcp_packet *packet, int code)
|
||||
{
|
||||
uint8_t *optionptr;
|
||||
int len;
|
||||
int rem;
|
||||
int overload = 0;
|
||||
enum {
|
||||
FILE_FIELD101 = FILE_FIELD * 0x101,
|
||||
SNAME_FIELD101 = SNAME_FIELD * 0x101,
|
||||
};
|
||||
|
||||
/* option bytes: [code][len][data1][data2]...[dataLEN] */
|
||||
optionptr = packet->options;
|
||||
rem = sizeof(packet->options);
|
||||
while (1) {
|
||||
if (rem <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (optionptr[OPT_CODE] == DHCP_PADDING) {
|
||||
rem--;
|
||||
optionptr++;
|
||||
continue;
|
||||
}
|
||||
if (optionptr[OPT_CODE] == DHCP_END) {
|
||||
if ((overload & FILE_FIELD101) == FILE_FIELD) {
|
||||
/* we can use packet->file */
|
||||
overload |= FILE_FIELD101;
|
||||
optionptr = packet->file;
|
||||
rem = sizeof(packet->file);
|
||||
continue;
|
||||
}
|
||||
if ((overload & SNAME_FIELD101) == SNAME_FIELD) {
|
||||
/* we can use packet->sname */
|
||||
overload |= SNAME_FIELD101;
|
||||
optionptr = packet->sname;
|
||||
rem = sizeof(packet->sname);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
len = OPT_DATA + optionptr[OPT_LEN];
|
||||
rem -= len;
|
||||
if (rem < 0)
|
||||
continue;
|
||||
|
||||
if (optionptr[OPT_CODE] == code)
|
||||
return optionptr + OPT_DATA;
|
||||
|
||||
if (optionptr[OPT_CODE] == DHCP_OVERLOAD)
|
||||
overload |= optionptr[OPT_DATA];
|
||||
|
||||
optionptr += len;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal handler
|
||||
* @param Signal value.
|
||||
*
|
||||
* @return Void.
|
||||
*/
|
||||
static void handler(int sig)
|
||||
{
|
||||
if (sig == SIGALRM)
|
||||
got_alarm = 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
426
src/scan_pppoe.c
Normal file
426
src/scan_pppoe.c
Normal file
@@ -0,0 +1,426 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Konstantin Vasin
|
||||
*
|
||||
* Licensed under GPLv2, see file LICENSE for more information.
|
||||
*/
|
||||
#include "scan_common.h"
|
||||
|
||||
/* PPPoE Codes. RFC 2516 */
|
||||
#define CODE_PADI 0x09
|
||||
#define CODE_PADO 0x07
|
||||
|
||||
/* PPPoE Tags */
|
||||
#define TAG_END_OF_LIST 0x0000
|
||||
#define TAG_SERVICE_NAME 0x0101
|
||||
#define TAG_AC_NAME 0x0102
|
||||
#define TAG_HOST_UNIQ 0x0103
|
||||
|
||||
/* PPPoE Default Tag and Version */
|
||||
#define PPPOE_TYPE_VERSION 0x11
|
||||
|
||||
/* PPPoE Packet including Ethernet headers */
|
||||
struct pppoe_packet {
|
||||
struct ethhdr eth_header; /* Ethernet header */
|
||||
uint8_t type_ver; /* PPPoE type and version */
|
||||
uint8_t code; /* PPPoE code */
|
||||
uint16_t sid; /* PPPoE session_ID */
|
||||
uint16_t len; /* Payload length */
|
||||
uint8_t payload[ETH_DATA_LEN]; /* PPPoE payload */
|
||||
};
|
||||
#define PPPOE_OVERHEAD 6 /* type, code, session, length */
|
||||
#define L2_HDR_SIZE (sizeof(struct ethhdr) + PPPOE_OVERHEAD)
|
||||
#define MAX_PPPOE_PAYLOAD (ETH_DATA_LEN - PPPOE_OVERHEAD)
|
||||
|
||||
/* PPPoE Tag */
|
||||
struct pppoe_tag {
|
||||
uint16_t type; /* Tag type */
|
||||
uint16_t len; /* Length of value */
|
||||
uint8_t payload[ETH_DATA_LEN]; /* Tag payload */
|
||||
};
|
||||
#define TAG_HDR_SIZE 4 /* Header size of a PPPoE tag */
|
||||
|
||||
static int pppoe_send_padi(int fd, int ifindex, uint8_t *hwaddr, const char *service, uint32_t id);
|
||||
static int pppoe_recv_pado(int fd, uint32_t id, uint8_t *hwaddr, json_t *result);
|
||||
static int pppoe_parse_packet(struct pppoe_packet *pkt, uint32_t id, json_t *result);
|
||||
static uint8_t *pppoe_extract_tag(struct pppoe_packet *pkt, uint16_t type, struct pppoe_tag *tag);
|
||||
static void handler(int sig);
|
||||
|
||||
static volatile sig_atomic_t got_alarm = 0;
|
||||
|
||||
/**
|
||||
* Search PPPoE server.
|
||||
*
|
||||
* @param ifname Network interface name
|
||||
* @param time Wait time.
|
||||
* @param result JSON object with result.
|
||||
*
|
||||
* @return 1 on success, 0 on time is out, -2 on invalid json object for result,
|
||||
* -1 on other errors.
|
||||
*/
|
||||
int scan_pppoe(const char *ifname, unsigned int time, json_t *result)
|
||||
{
|
||||
int ifindex;
|
||||
uint8_t hwaddr[ETH_ALEN];
|
||||
struct sigaction sa;
|
||||
uid_t euid;
|
||||
uint32_t id;
|
||||
const char *service;
|
||||
int fd = -1;
|
||||
int ret = -1;
|
||||
|
||||
/* Check pointer for result */
|
||||
if (!json_is_object(result)) {
|
||||
ret = - 2;
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
/* Check superuser priveleges */
|
||||
if ((euid = geteuid()) != 0) {
|
||||
s_set_err(result, "You must be root");
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
/* Check input data */
|
||||
/* Check wait time */
|
||||
if (time == 0)
|
||||
time = 1;
|
||||
if (time > MAX_WAIT_TIME) {
|
||||
s_set_err(result, "Max wait time is %d", MAX_WAIT_TIME);
|
||||
goto err_exit;
|
||||
}
|
||||
/* Check interface name */
|
||||
if (!ifname || *ifname == '\0') {
|
||||
s_set_err(result, "Empty interface name");
|
||||
goto err_exit;
|
||||
}
|
||||
/* Check interface name length */
|
||||
if (strlen(ifname) > IFNAMSIZ) {
|
||||
s_set_err(result, "Interface name is too long");
|
||||
goto err_exit;
|
||||
}
|
||||
/* Get interface index */
|
||||
if ((ifindex = s_get_ifindex(ifname)) == 0) {
|
||||
s_set_err(result, "No interface found");
|
||||
goto err_exit;
|
||||
}
|
||||
/* Get MAC address */
|
||||
if ((s_get_hw_address(ifname, hwaddr)) < 0) {
|
||||
s_set_err(result, "Can't get MAC address");
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
/* Create socket */
|
||||
fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PPP_DISC));
|
||||
|
||||
if (fd < 0) {
|
||||
s_set_err(result, "Can't create socket");
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
/* Establish handler for notification signal */
|
||||
memset(&sa, 0, sizeof(struct sigaction));
|
||||
sa.sa_flags = 0;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_handler = handler;
|
||||
if ((sigaction(SIGALRM, &sa, NULL)) == -1) {
|
||||
s_set_err(result, "Can't set handler for timer");
|
||||
goto err_exit2;
|
||||
}
|
||||
|
||||
/* Try extract service name */
|
||||
service = json_string_value(json_object_get(result, "service"));
|
||||
|
||||
/* Send Request */
|
||||
id = s_random_id();
|
||||
ret = pppoe_send_padi(fd, ifindex, hwaddr, service, id);
|
||||
if (ret < 0) {
|
||||
if (ret == -2)
|
||||
s_set_err(result, "Very long service name");
|
||||
else
|
||||
s_set_err(result, "Can't send request");
|
||||
ret = -1;
|
||||
goto err_exit2;
|
||||
}
|
||||
|
||||
json_object_clear(result);
|
||||
|
||||
/* Start timer */
|
||||
alarm(time);
|
||||
|
||||
/* Loop, recv packets && search answer for us */
|
||||
while(1) {
|
||||
ret = pppoe_recv_pado(fd, id, hwaddr, result);
|
||||
|
||||
/* Answer received */
|
||||
if (ret == 0) {
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
/* Read error */
|
||||
if (ret == -1) {
|
||||
s_set_err(result, "Read socket error");
|
||||
break;
|
||||
}
|
||||
/* Check timer */
|
||||
if (got_alarm == 1) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Cancel timer */
|
||||
alarm(0);
|
||||
err_exit2:
|
||||
close(fd);
|
||||
err_exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search PPPoE server.
|
||||
*
|
||||
* @param ifname Network interface name
|
||||
* @param time Wait time.
|
||||
*
|
||||
* @return 1 on success, 0 on time is out, -1 on error.
|
||||
*/
|
||||
int scan_pppoe_result(const char *ifname, unsigned int time) {
|
||||
|
||||
int ret;
|
||||
json_t *result = json_object();
|
||||
|
||||
ret = scan_pppoe(ifname, time, result);
|
||||
if (ret < 0)
|
||||
ret = -1;
|
||||
|
||||
json_decref(result);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send PPPoE PADI
|
||||
*
|
||||
* @param fd Socket descriptor.
|
||||
* @param ifindex Interface index.
|
||||
* @param hwaddr MAC address of interface.
|
||||
* @param service Service Name.
|
||||
* @param id Host Uniq.
|
||||
*
|
||||
* @return Number of bytes send on success, -1 on error, -2 on too long service name.
|
||||
*/
|
||||
static int pppoe_send_padi(int fd, int ifindex, uint8_t *hwaddr, const char *service, uint32_t id)
|
||||
{
|
||||
struct sockaddr_ll sa;
|
||||
struct pppoe_packet packet;
|
||||
struct pppoe_tag *tag_hu = (struct pppoe_tag *)&packet.payload;
|
||||
struct pppoe_tag sv_name;
|
||||
unsigned char *cursor = packet.payload;
|
||||
uint16_t pack_len;
|
||||
uint16_t namelen = 0;
|
||||
int result = -1;
|
||||
|
||||
/* Bind */
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sll_family = AF_PACKET;
|
||||
sa.sll_protocol = htons(ETH_P_PPP_DISC);
|
||||
sa.sll_ifindex = ifindex;
|
||||
|
||||
if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/* fill packet */
|
||||
memset(&packet, 0, sizeof(struct pppoe_packet));
|
||||
/* ethernet fields */
|
||||
memset(packet.eth_header.h_dest, 0xff, ETH_ALEN);
|
||||
memcpy(packet.eth_header.h_source, hwaddr, ETH_ALEN);
|
||||
packet.eth_header.h_proto = htons(ETH_P_PPP_DISC);
|
||||
/* pppoe fields */
|
||||
packet.type_ver = PPPOE_TYPE_VERSION;
|
||||
packet.code = CODE_PADI;
|
||||
packet.sid = 0;
|
||||
|
||||
/* PPPoE Tags */
|
||||
/* Add Host-Uniq value */
|
||||
tag_hu->type = htons(TAG_HOST_UNIQ);
|
||||
tag_hu->len = htons(sizeof(uint32_t));
|
||||
memcpy(tag_hu->payload, &id, sizeof(id));
|
||||
cursor += (TAG_HDR_SIZE + sizeof(id));
|
||||
pack_len = sizeof(id) + TAG_HDR_SIZE;
|
||||
|
||||
/* Service name */
|
||||
if (service) {
|
||||
namelen = (uint16_t) strlen(service);
|
||||
}
|
||||
if (namelen < MAX_PPPOE_PAYLOAD - pack_len) {
|
||||
pack_len += TAG_HDR_SIZE + namelen;
|
||||
sv_name.type = htons(TAG_SERVICE_NAME);
|
||||
sv_name.len = htons(namelen);
|
||||
|
||||
if (service) {
|
||||
memcpy(&sv_name.payload, service, namelen);
|
||||
}
|
||||
memcpy(cursor, &sv_name, namelen + TAG_HDR_SIZE);
|
||||
} else {
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Add pppoe packet length value */
|
||||
packet.len = htons(pack_len);
|
||||
|
||||
/* Send packet */
|
||||
result = send(fd, &packet, (int)(pack_len + L2_HDR_SIZE), 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Recv PADO.
|
||||
*
|
||||
* @param fd Socket descriptor.
|
||||
* @param id Host Uniq.
|
||||
* @param hwaddr - MAC address of interface.
|
||||
* @param result JSON object for result.
|
||||
*
|
||||
* @return -1 on read error, 0 on success, -2 if packet is not correct,
|
||||
* -3 if time is out.
|
||||
*/
|
||||
static int pppoe_recv_pado(int fd, uint32_t id, uint8_t *hwaddr, json_t *result)
|
||||
{
|
||||
struct pppoe_packet pkt;
|
||||
int bytes;
|
||||
|
||||
memset(&pkt, 0, sizeof(struct pppoe_packet));
|
||||
|
||||
/* Read packet */
|
||||
bytes = read(fd, &pkt, sizeof(pkt));
|
||||
|
||||
if (bytes < 0) {
|
||||
if (errno == EINTR) {
|
||||
return -3;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check length */
|
||||
if ((ntohs(pkt.len) + PPPOE_OVERHEAD) > ETH_DATA_LEN)
|
||||
return -2;
|
||||
|
||||
/* Check protocol */
|
||||
if (ETH_P_PPP_DISC != ntohs(pkt.eth_header.h_proto))
|
||||
return -2;
|
||||
|
||||
/* Check pppoe type and version */
|
||||
if (PPPOE_TYPE_VERSION != (pkt.type_ver))
|
||||
return -2;
|
||||
|
||||
/* Check pado code in pppoe packet */
|
||||
if (CODE_PADO != pkt.code)
|
||||
return -2;
|
||||
|
||||
/* Check dest MAC address */
|
||||
if (memcmp(pkt.eth_header.h_dest, hwaddr, ETH_ALEN))
|
||||
return -2;
|
||||
|
||||
if (pppoe_parse_packet(&pkt, id, result))
|
||||
return -2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse PPPoE packet.
|
||||
*
|
||||
* @param pkt PPPoE packet.
|
||||
* @param id Host Uniq.
|
||||
* @param result JSON object for result.
|
||||
*
|
||||
* @return zero on success, -1 on error.
|
||||
*/
|
||||
static int pppoe_parse_packet(struct pppoe_packet *pkt, uint32_t id, json_t *result)
|
||||
{
|
||||
struct pppoe_tag tag;
|
||||
uint32_t host_uniq;
|
||||
char buf[ETH_DATA_LEN];
|
||||
|
||||
memset(&tag, 0, sizeof(struct pppoe_tag));
|
||||
|
||||
/* Check Host Uniq */
|
||||
if (pppoe_extract_tag(pkt, TAG_HOST_UNIQ, &tag)) {
|
||||
memcpy(&host_uniq, tag.payload, tag.len);
|
||||
if (host_uniq != id)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Extract AC Name */
|
||||
if (pppoe_extract_tag(pkt, TAG_AC_NAME, &tag)) {
|
||||
if (tag.len < ETH_DATA_LEN) {
|
||||
memcpy(buf, tag.payload, tag.len);
|
||||
buf[tag.len] = '\0';
|
||||
}
|
||||
json_object_set_new_nocheck(result, "ac", json_string(buf));
|
||||
}
|
||||
|
||||
/* Extract Service Name */
|
||||
if (pppoe_extract_tag(pkt, TAG_SERVICE_NAME, &tag)) {
|
||||
if (tag.len < ETH_DATA_LEN) {
|
||||
memcpy(buf, tag.payload, tag.len);
|
||||
buf[tag.len] = '\0';
|
||||
}
|
||||
json_object_set_new_nocheck(result, "service", json_string(buf));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extract TAG.
|
||||
*
|
||||
* @param pkt PPPoE packet.
|
||||
* @param type Required tag type.
|
||||
* @param tag Buffer for tag.
|
||||
*
|
||||
* @return Pointer on tag value if tag is found, NULL otherwise.
|
||||
*/
|
||||
static uint8_t *pppoe_extract_tag(struct pppoe_packet *pkt, uint16_t type, struct pppoe_tag *tag)
|
||||
{
|
||||
uint16_t len = ntohs(pkt->len);
|
||||
uint8_t *cur_tag;
|
||||
|
||||
cur_tag = pkt->payload;
|
||||
|
||||
while ((cur_tag - pkt->payload) < len) {
|
||||
/* Alignment */
|
||||
uint16_t tag_type = (((uint16_t) cur_tag[0]) << 8) + (uint16_t) cur_tag[1];
|
||||
uint16_t tag_len = (((uint16_t) cur_tag[2]) << 8) + (uint16_t) cur_tag[3];
|
||||
|
||||
if (TAG_END_OF_LIST == tag_type)
|
||||
return NULL;
|
||||
|
||||
if (((cur_tag - pkt->payload) + tag_len + TAG_HDR_SIZE) > len )
|
||||
return NULL;
|
||||
|
||||
if (type == tag_type) {
|
||||
tag->type = tag_type;
|
||||
tag->len = tag_len;
|
||||
cur_tag += TAG_HDR_SIZE;
|
||||
memcpy(tag->payload, cur_tag, tag_len);
|
||||
return cur_tag;
|
||||
}
|
||||
cur_tag = cur_tag + TAG_HDR_SIZE + tag_len;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal handler
|
||||
* @param Signal value.
|
||||
*
|
||||
* @return Void.
|
||||
*/
|
||||
static void handler(int sig)
|
||||
{
|
||||
if (sig == SIGALRM)
|
||||
got_alarm = 1;
|
||||
}
|
||||
Reference in New Issue
Block a user