Commit e7a0b106 authored by Juliusz Chroboczek's avatar Juliusz Chroboczek

Initial import.

parents
CDEBUGFLAGS = -Os -g -Wall
DEFINES = $(PLATFORM_DEFINES)
CFLAGS = -I/usr/include/libnl3 $(CDEBUGFLAGS) $(DEFINES) $(EXTRA_DEFINES)
LDLIBS = -lnl-genl-3 -lnl-route-3 -lnl-3
SRCS = sroamd.c client.c lease.c ra.c dhcpv4.c interface.c netlink.c \
flood.c prefix.c util.c
OBJS = sroamd.o client.o lease.o ra.o dhcpv4.o interface.o netlink.o \
flood.o prefix.o util.o
sroamd: $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) -o sroamd $(OBJS) $(LDLIBS)
.PHONY: clean
clean:
-rm -f sroamd *.o *~ core TAGS gmon.out
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#include <arpa/inet.h>
#include <netlink/errno.h>
#include "interface.h"
#include "client.h"
#include "netlink.h"
struct client *clients;
int numclients = 0, maxclients = 0;
struct client *
find_client(const unsigned char *mac)
{
for(int i = 0; i < numclients; i++) {
if(memcmp(clients[i].mac, mac, 6) == 0)
return &clients[i];
}
return NULL;
}
struct client *
add_client(struct interface *interface, const unsigned char *mac)
{
struct client *client;
client = find_client(mac);
if(client != NULL)
return client;
if(maxclients <= numclients) {
int n = maxclients == 0 ? 8 : 2 * maxclients;
struct client *newclients =
realloc(clients, n * sizeof(struct client));
if(newclients != NULL) {
clients = newclients;
maxclients = n;
}
}
if(maxclients <= numclients)
return NULL;
memset(&clients[numclients], 0, sizeof(struct client));
clients[numclients].interface = interface;
memcpy(clients[numclients].mac, mac, 6);
numclients++;
return &clients[numclients - 1];
}
int
flush_client(const unsigned char *mac)
{
int i;
struct client *client;
client = find_client(mac);
if(client == NULL)
return 0;
i = client - clients;
assert(i >= 0 && i < numclients);
update_client_route(client, NULL, 0);
update_client_route(client, NULL, 1);
if(i < numclients - 1)
memmove(clients + i, clients + i + 1, numclients - i - 1);
numclients--;
return 1;
}
static const char zeroes[8] = {0};
void
update_client_routes(const unsigned char *mac,
const unsigned char *addr, int ipv6)
{
for(int i = 0; i < numclients; i++) {
if(memcmp(clients[i].mac, mac, 6) == 0)
update_client_route(&clients[i], addr, ipv6);
}
}
void
client_cleanup()
{
for(int i = 0; i < numclients; i++) {
update_client_route(&clients[i], NULL, 0);
update_client_route(&clients[i], NULL, 1);
}
}
static const char zeroes[8];
int
update_client_route(struct client *client, const unsigned char *addr, int ipv6)
{
int rc;
if(ipv6) {
unsigned char buf[16];
if((addr == NULL && memcmp(client->ipv6, zeroes, 8) == 0) ||
(addr != NULL && memcmp(client->ipv6, addr, 8) == 0))
return 0;
if(memcmp(client->ipv6, zeroes, 8) != 0) {
memcpy(buf, client->ipv6, 8);
memset(buf + 8, 0, 8);
netlink_route(client->interface->ifindex, 0, 1, buf, 64);
memset(client->ipv6, 0, 8);
}
if(addr != NULL) {
memcpy(buf, addr, 8);
memset(buf + 8, 0, 8);
rc = netlink_route(client->interface->ifindex, 1, 1, buf, 64);
if(rc < 0 && rc != -NLE_EXIST) {
nl_perror(rc, "netlink_route");
return rc;
}
memcpy(client->ipv6, addr, 8);
} else {
memcpy(client->ipv6, zeroes, 8);
}
} else {
if((addr == NULL && memcmp(client->ipv4, zeroes, 4) == 0) ||
(addr != NULL && memcmp(client->ipv4, addr, 4) == 0))
return 0;
if(memcmp(client->ipv4, zeroes, 4) != 0) {
netlink_route(client->interface->ifindex, 0, 0, client->ipv4, 32);
memset(client->ipv4, 0, 4);
}
if(addr != NULL) {
rc = netlink_route(client->interface->ifindex, 1, 0, addr, 32);
if(rc < 0 && rc != -NLE_EXIST) {
nl_perror(rc, "netlink_route");
return rc;
}
memcpy(client->ipv4, addr, 4);
} else {
memcpy(client->ipv4, zeroes, 4);
}
}
return 1;
}
struct client {
struct interface *interface;
unsigned char mac[6];
unsigned char ipv4[4];
unsigned char ipv6[8];
};
extern struct client *clients;
extern int numclients, maxclients;
struct client *find_client(const unsigned char *mac);
struct client *add_client(struct interface *interface, const unsigned char *mac);
int flush_client(const unsigned char *mac);
int update_client_route(struct client *client,
const unsigned char *addr, int ipv6);
void update_client_routes(const unsigned char *mac,
const unsigned char *addr, int ipv6);
void client_cleanup(void);
This diff is collapsed.
/*
Copyright (c) 2015 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
extern unsigned char dnsv4[16][4];
extern int numdnsv4;
extern int dhcpv4_socket;
int dhcpv4_setup(const unsigned char *addr);
int dhcpv4_receive(void);
void dhcpv4_cleanup(void);
int send_gratuitious_arp(const unsigned char *myaddr,
struct interface *interface,
const unsigned char *mac);
This diff is collapsed.
struct datum {
unsigned short seqno;
unsigned char keylen;
unsigned char vallen;
time_t time;
unsigned char datum[];
};
static inline const unsigned char *
datum_key(const struct datum *datum)
{
return datum->datum;
}
static inline const unsigned char *
datum_val(const struct datum *datum)
{
return datum->datum + datum->keylen;
}
extern int flood_port;
extern int flood_socket;
extern struct datum **data;
extern int numdata, maxdata;
extern struct timespec flood_time;
struct unacked {
int count;
unsigned char *key;
int keylen;
time_t time;
};
struct buffered {
unsigned char *key;
int keylen;
int acked;
};
#define MAXBUFFERED 100
struct neighbour {
struct sockaddr_in6 addr;
struct in6_pktinfo *pktinfo;
int permanent;
time_t time;
time_t send_time;
struct unacked *unacked;
int numunacked, maxunacked;
struct buffered *buffered;
int numbuffered;
int dump_request_count;
int dump_done;
};
extern struct neighbour *neighbours;
extern int numneighbours, maxneighbours;
struct datum *find_datum(const unsigned char *key, int keylen);
struct datum *update_datum(const unsigned char *key, int keylen,
unsigned short seqno,
const unsigned char *val, int vallen,
int time, int *updated, int *conflict);
void flush_datum(struct datum *datum);
time_t datum_remaining(const struct datum *datum);
int extend_datum(struct datum *datum, time_t extend);
int flood_setup(void (*callback)(struct datum *, int));
void flood_cleanup(void);
int flood_listen(void);
struct neighbour *
find_neighbour(struct sockaddr_in6 *sin6, int create, int update, int permanent);
void flood(struct datum *datum, struct neighbour *neigh, int ack, int doit);
void periodic_flood(void);
int flush_updates(struct neighbour *neigh, int all);
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <ifaddrs.h>
#include "interface.h"
struct interface *interfaces = NULL;
int numinterfaces = 0;
struct interface *
find_interface(int ifindex)
{
for(int i = 0; i < numinterfaces; i++) {
if(interfaces[i].ifindex == ifindex)
return &interfaces[i];
}
return NULL;
}
int
interface_v4(struct interface *interface, unsigned char *v4_return)
{
struct ifreq req;
int s, rc;
s = socket(PF_INET, SOCK_DGRAM, 0);
if(s < 0)
return -1;
memset(&req, 0, sizeof(req));
strncpy(req.ifr_name, interface->ifname, sizeof(req.ifr_name));
req.ifr_addr.sa_family = AF_INET;
rc = ioctl(s, SIOCGIFADDR, &req);
if(rc < 0) {
close(s);
return -1;
}
close(s);
memcpy(v4_return, &((struct sockaddr_in*)&req.ifr_addr)->sin_addr, 4);
return 1;
}
int
interface_v6(struct interface *interface, unsigned char *v6_return)
{
struct ifaddrs *ifaddr;
int rc, found = 0;
rc = getifaddrs(&ifaddr);
if(rc < 0)
return rc;
for(struct ifaddrs *ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
struct sockaddr_in6 *sin6;
if(ifa->ifa_addr == NULL ||
strcmp(ifa->ifa_name, interface->ifname) != 0 ||
ifa->ifa_addr->sa_family != AF_INET6)
continue;
sin6 = (struct sockaddr_in6*)ifa->ifa_addr;
if(!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
continue;
memcpy(v6_return, &sin6->sin6_addr, 16);
found = 1;
break;
}
freeifaddrs(ifaddr);
if(!found) {
errno = ESRCH;
return -1;
}
return 1;
}
int
sock_bindtodevice(int s, struct interface *interface)
{
struct ifreq ifr;
if(interface != NULL) {
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, interface->ifname, IFNAMSIZ);
return setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr));
} else {
return setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, NULL, 0);
}
}
struct interface {
char *ifname;
int ifindex;
unsigned char mac[6];
};
extern struct interface *interfaces;
extern int numinterfaces, maxinterfaces;
struct interface *find_interface(int ifindex);
int interface_v4(struct interface *interface, unsigned char *v4_return);
int interface_v6(struct interface *interface, unsigned char *v6_return);
int sock_bindtodevice(int s, struct interface *interface);
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <arpa/inet.h>
#include "prefix.h"
#include "client.h"
#include "flood.h"
#include "lease.h"
unsigned char myid[8];
#define LEASE_FUDGE 10
unsigned char v4prefix[4], v6prefix[16];
int v4plen = -1, v6plen = -1;
const unsigned char zeroes[8] = {0};
struct datum *
find_lease(const unsigned char *mac, int ipv6)
{
struct datum *datum = NULL;
/* In general, multiple leases can point to the same MAC. Pick the
one that's lexicographically smallest. Perhaps we should be
routing all of the addresses instead? */
for(int i = 0; i < numdata; i++) {
if(data[i]->keylen < 1)
continue;
if(datum_key(data[i])[0] !=
(ipv6 ? DATUM_IPv6_LEASE : DATUM_IPv4_LEASE) ||
data[i]->keylen != (ipv6 ? 9 : 5))
continue;
if(data[i]->vallen != 6 || memcmp(datum_val(data[i]), mac, 6) != 0)
continue;
if(datum == NULL || memcmp(datum_val(data[i]), datum_val(datum), 6) < 0)
datum = data[i];
}
return datum;
}
struct datum *
find_lease_by_ip(const unsigned char *a, int ipv6)
{
if(ipv6) {
for(int i = 0; i < numdata; i++) {
if(data[i]->keylen == 9 &&
datum_key(data[i])[0] == DATUM_IPv6_LEASE &&
memcmp(datum_key(data[i]) + 1, a, 8) == 0)
return data[i];
}
} else {
for(int i = 0; i < numdata; i++) {
if(data[i]->keylen == 5 &&
datum_key(data[i])[0] == DATUM_IPv4_LEASE &&
memcmp(datum_key(data[i]) + 1, a, 4) == 0)
return data[i];
}
}
return NULL;
}
static struct datum *
make_lease(const unsigned char *suggested, const unsigned char *mac, int ipv6)
{
struct datum *datum = NULL;
unsigned char key[9];
struct timespec now;
int ok;
unsigned char addr[8];
clock_gettime(CLOCK_MONOTONIC, &now);
if(suggested)
datum = find_lease_by_ip(suggested, ipv6);
if(datum != NULL && datum->time + LEASE_FUDGE < now.tv_sec &&
datum->vallen == 6) {
memcpy((char*)datum_val(datum), mac, 6);
return datum;
} else {
datum = NULL;
}
ok = 0;
for(int i = 0; i < 32; i++) {
int rc;
if(!ipv6)
rc = random_prefix(v4prefix, v4plen, addr, 32);
else
rc = random_prefix(v6prefix, v6plen, addr, 64);
if(rc >= 0 && find_lease(addr, ipv6) == NULL) {
ok = 1;
break;
}
}
if(!ok)
return NULL;
key[0] = ipv6 ? DATUM_IPv6_LEASE : DATUM_IPv4_LEASE;
memcpy(key + 1, addr, ipv6 ? 8 : 4);
return update_datum(key, ipv6 ? 9 : 5, 0, mac, 6, 0, NULL, NULL);
}
struct datum *
update_lease(const unsigned char *mac, int ipv6,
const unsigned char *suggested,
int time, int *doit_return)
{
struct datum *datum;
int doit;
datum = find_lease(mac, ipv6);
if(datum != NULL) {
doit = extend_datum(datum, time);
if(doit_return)
*doit_return = doit;
flood(datum, NULL, 0, 1);
return datum;
}
if ((!ipv6 && (v4plen < 0 || v4plen > 32)) ||
(ipv6 && (v6plen < 0 || v6plen > 64))) {
return NULL;
}
datum = make_lease(suggested, mac, ipv6);
if(datum == NULL)
return NULL;
doit = extend_datum(datum, time);
if(doit_return)
*doit_return = doit;
update_client_routes(mac, lease_address(datum, ipv6), ipv6);
flood(datum, NULL, 0, 1);
return datum;
}
const unsigned char *
lease_address(const struct datum *datum, int ipv6)
{
if(datum_key(datum)[0] != (ipv6 ? DATUM_IPv6_LEASE : DATUM_IPv4_LEASE))
return NULL;
if(datum->keylen != (ipv6 ? 9 : 5))
return NULL;
return datum_key(datum) + 1;
}
void
update_lease_routes(const struct datum *datum)
{
int ipv6;
if(datum->keylen < 1)
return;
if(datum_key(datum)[0] == DATUM_IPv6_LEASE)
ipv6 = 1;
else if(datum_key(datum)[0] == DATUM_IPv4_LEASE)
ipv6 = 0;
else
return;
if(datum->keylen != (ipv6 ? 9 : 5) || datum->vallen != 6)
return;
for(int i = 0; i < numclients; i++) {
int match;
if(ipv6)
match = memcmp(clients[i].ipv6, datum_key(datum) + 1, 8) == 0;
else
match = memcmp(clients[i].ipv4, datum_key(datum) + 1, 4) == 0;
if(match && memcmp(clients[i].mac, datum_val(datum), 6) != 0) {
update_client_route(&clients[i], NULL, ipv6);
}
}
}
struct client *
update_association(struct interface *interface, const unsigned char *mac,
int time)
{
unsigned char key[1 + 6];
struct datum *datum;
int seqno = 0;
struct client *client;
client = add_client(interface, mac);
if(client == NULL)
return NULL;
key[0] = DATUM_ASSOCIATED;
memcpy(key + 1, mac, 6);
datum = find_datum(key, 7);
if(datum != NULL) {
if(datum->vallen == 8 &&
memcmp(datum_val(datum), myid, 8) == 0) {
extend_datum(datum, time);
flood(datum, NULL, 0, 1);
return client;
} else {
seqno = datum->seqno + 1;
}
}
datum = update_datum(key, 7, seqno, myid, 8, time, NULL, NULL);
flood(datum, NULL, 0, 1);
return client;
}
void
flush_association(const unsigned char *mac, int time)
{
unsigned char key[1 + 6];
struct datum *datum;
int seqno;
flush_client(mac);
key[0] = DATUM_ASSOCIATED;
memcpy(key + 1, mac, 6);
datum = find_datum(key, 7);
if(datum == NULL || datum->vallen != 8 ||
memcmp(datum_val(datum), myid, 8) != 0)
return;
seqno = datum->seqno + 1;
datum = update_datum(key, 7, seqno, NULL, 0, time, NULL, NULL);
flood(datum, NULL, 0, 1);
}
#define DATUM_IPv4_LEASE 0
#define DATUM_IPv6_LEASE 1
#define DATUM_ASSOCIATED 2
#define ASSOCIATION_TIME 5400
extern unsigned char myid[8];
extern unsigned char v4prefix[4], v6prefix[16];
extern int v4plen, v6plen;
struct datum *find_lease(const unsigned char *mac, int ipv6);
struct datum *find_lease_by_ip(const unsigned char *a, int ipv6);
struct datum *update_lease(const unsigned char *mac, int ipv6,
const unsigned char *suggested,
int time, int *doit_return);
const unsigned char *lease_address(const struct datum *datum, int ipv6);
void update_lease_routes(const struct datum *datum);
struct client *update_association(struct interface *interface,
const unsigned char *mac, int time);
void flush_association(const unsigned char *mac, int time);
void datum_notify(struct datum *datum);
#include <stdlib.h>
#include <errno.h>
#include <net/if.h>
#include <linux/nl80211.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include <netlink/route/addr.h>
#include <netlink/route/route.h>
#include <netlink/errno.h>
#include "netlink.h"
struct nl_sock *nl_sock;
int nl80211_id;
struct nl_cb *nl_cb;
static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
void *arg)
{
int *ret = arg;
*ret = err->error;
return NL_STOP;
}
static int ack_handler(struct nl_msg *msg, void *arg)
{
int *ret = arg;
*ret = 0;
return NL_STOP;
}
struct handler_args {
const char *group;
int id;
};
static int family_handler(struct nl_msg *msg, void *arg)
{
struct handler_args *grp = arg;
struct nlattr *tb[CTRL_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *mcgrp;
int rem_mcgrp;
nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (!tb[CTRL_ATTR_MCAST_GROUPS])
return NL_SKIP;
nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mcgrp) {
struct nlattr *tb_mcgrp[CTRL_ATTR_MCAST_GRP_MAX + 1];
nla_parse(tb_mcgrp, CTRL_ATTR_MCAST_GRP_MAX,
nla_data(mcgrp), nla_len(mcgrp), NULL);
if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME] ||
!tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID])
continue;
if (strncmp(nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]),
grp->group, nla_len(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME])))
continue;
grp->id = nla_get_u32(tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]);
break;
}
return NL_SKIP;
}
int nl_get_multicast_id(struct nl_sock *sock,
const char *family, const char *group)
{
struct nl_msg *msg;
struct nl_cb *cb;
int ret, ctrlid;
struct handler_args grp = {
.group = group,
.id = -ENOENT,
};
msg = nlmsg_alloc();
if (!msg)
return -ENOMEM;
cb = nl_cb_alloc(NL_CB_DEFAULT);
if (!cb) {
ret = -ENOMEM;
goto out_fail_cb;
}
ctrlid = genl_ctrl_resolve(sock, "nlctrl");
genlmsg_put(msg, 0, 0, ctrlid, 0,
0, CTRL_CMD_GETFAMILY, 0);
ret = -ENOBUFS;
NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family);
ret = nl_send_auto(sock, msg);
if (ret < 0)
goto out;
ret = 1;
nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &ret);
nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &ret);
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, family_handler, &grp);
while (ret > 0)
nl_recvmsgs(sock, cb);
if (ret == 0)
ret = grp.id;
nla_put_failure:
out:
nl_cb_put(cb);
out_fail_cb:
nlmsg_free(msg);
return ret;
}
static int
ok_handler(struct nl_msg *msg, void *arg)
{
return NL_OK;
}
static int
event_handler(struct nl_msg *msg, void *arg)
{
netlink_callback cb = arg;
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *tb[NL80211_ATTR_MAX + 1];
char ifname[100];
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if(tb[NL80211_ATTR_IFINDEX] == NULL)
return NL_OK;
if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), ifname);
if(gnlh->cmd != NL80211_CMD_NEW_STATION &&
gnlh->cmd != NL80211_CMD_DEL_STATION) {
return NL_OK;
}
if(cb != NULL)
cb(gnlh->cmd == NL80211_CMD_NEW_STATION,
nla_get_u32(tb[NL80211_ATTR_IFINDEX]),
nla_data(tb[NL80211_ATTR_MAC]));
return NL_OK;
}
int
netlink_init(netlink_callback cb)
{
int rc, mcid;
nl_sock = nl_socket_alloc();
if(nl_sock == NULL)
return -1;
rc = genl_connect(nl_sock);
if(rc < 0)
return -1;
nl_socket_set_buffer_size(nl_sock, 8192, 8192);
nl80211_id = genl_ctrl_resolve(nl_sock, "nl80211");
if(nl80211_id < 0)
return -1;
mcid = nl_get_multicast_id(nl_sock, "nl80211", "mlme");
if(mcid < 0)
return -1;
rc = nl_socket_add_membership(nl_sock, mcid);
if(rc < 0)
return -1;
nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
if(nl_cb == NULL)
return -1;
nl_cb_set(nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, ok_handler, NULL);
nl_cb_set(nl_cb, NL_CB_VALID, NL_CB_CUSTOM, event_handler, cb);
rc = nl_socket_set_nonblocking(nl_sock);
if(rc < 0)
return -1;
return 1;
}
int
netlink_dump(int ifindex)
{
struct nl_msg *msg;
int rc;
msg = nlmsg_alloc();
if(msg == NULL)
return -1;
genlmsg_put(msg, 0, 0, nl80211_id, 0, NLM_F_DUMP,
NL80211_CMD_GET_STATION, 0);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
rc = nl_send_auto(nl_sock, msg);
nlmsg_free(msg);
if(rc < 0)
return rc;
return 1;
nla_put_failure:
return -1;
}
int
netlink_listen()
{
return nl_recvmsgs(nl_sock, nl_cb);
}
int
netlink_socket()
{
return nl_socket_get_fd(nl_sock);
}
int
netlink_disassociate(int ifindex, const unsigned char *mac,
const unsigned char *mymac)
{
struct nl_msg *msg;
int rc;
#if 0
unsigned char buf[26];
msg = nlmsg_alloc();
if(msg == NULL)
return -1;
genlmsg_put(msg, 0, 0, nl80211_id, 0, 0, NL80211_CMD_FRAME, 0);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
memset(buf, 0, 26);
buf[0] = 10 << 4; /* FC */
memcpy(buf + 4, mac, 6); /* da */
memcpy(buf + 10, mymac, 6); /* sa */
memcpy(buf + 16, mymac, 6); /* bssid */
buf[24] = 1; /* reason */
buf[25] = 0;
NLA_PUT(msg, NL80211_ATTR_FRAME, 26, buf);
NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
rc = nl_send_auto(nl_sock, msg);
nlmsg_free(msg);
if(rc < 0)
return rc;
#endif
msg = nlmsg_alloc();
if(msg == NULL)
return -1;
genlmsg_put(msg, 0, 0, nl80211_id, 0, 0, NL80211_CMD_DEL_STATION, 0);
NLA_PUT(msg, NL80211_ATTR_MAC, 6, mac);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
NLA_PUT_U8(msg, NL80211_ATTR_MGMT_SUBTYPE, 0x0a);
NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, 1);
rc = nl_send_auto(nl_sock, msg);
nlmsg_free(msg);
if(rc < 0)
return rc;
return 1;
nla_put_failure:
return -1;
}
struct nl_sock *rtnl_sock = NULL;
int
netlink_route(int ifindex, int add, int ipv6, const unsigned char *dst, int dlen)
{
struct rtnl_route *route = NULL;
struct nl_addr *addr = NULL;
struct rtnl_nexthop *nh = NULL;
unsigned char dest[16];
int rc;
if(rtnl_sock == NULL) {
rtnl_sock = nl_socket_alloc();
if(rtnl_sock == NULL)
return -NLE_NOMEM;
rc = nl_connect(rtnl_sock, NETLINK_ROUTE);
if(rc < 0) {
nl_socket_free(rtnl_sock);
rtnl_sock = NULL;
return rc;
}
}
memcpy(dest, dst, ipv6 ? 16 : 4);
addr = nl_addr_alloc(ipv6 ? 16 : 4);
if(addr == NULL) {
rc = NLE_NOMEM;
goto fail;
}
nl_addr_set_family(addr, ipv6 ? AF_INET6 : AF_INET);
nl_addr_set_binary_addr(addr, dest, ipv6 ? 16 : 4);
nl_addr_set_prefixlen(addr, dlen);
route = rtnl_route_alloc();
if(route == NULL) {
rc = NLE_NOMEM;
goto fail;
}
rc = rtnl_route_set_family(route, ipv6 ? AF_INET6 : AF_INET);
if(rc < 0)
goto fail;
rc = rtnl_route_set_dst(route, addr);
if(rc < 0)
goto fail;
nh = rtnl_route_nh_alloc();
if(nh == NULL) {
rc = NLE_NOMEM;
goto fail;
}
rtnl_route_nh_set_ifindex(nh, ifindex);
#if 0
rtnl_route_nh_set_gateway(nh, addr);
rtnl_route_nh_set_flags(nh, RTNH_F_ONLINK);
#endif
rtnl_route_add_nexthop(route, nh);
nh = NULL;
rtnl_route_set_protocol(route, 44);
if(add)
rc = rtnl_route_add(rtnl_sock, route, 0);
else
rc = rtnl_route_delete(rtnl_sock, route, 0);
if(rc < 0) {
goto fail;
}
rtnl_route_put(route);
nl_addr_put(addr);
return 1;
fail:
if(route != NULL)
rtnl_route_put(route);
if(nh != NULL)
rtnl_route_nh_free(nh);
if(addr != NULL)
nl_addr_put(addr);
return rc;
}
typedef void (*netlink_callback)(int add, int ifindex, const unsigned char *mac);
int netlink_init(netlink_callback cb);
int netlink_dump(int ifindex);
int netlink_listen(void);
int netlink_socket(void);
int netlink_disassociate(int ifindex, const unsigned char *mac,
const unsigned char *mymac);
int netlink_route(int ifindex, int add, int ipv6,
const unsigned char *dst, int dlen);
#include <stdlib.h>
#include <string.h>
#include "prefix.h"
int
in_prefix(const unsigned char *a, const unsigned char *p, int plen)
{
if(memcmp(a, p, plen / 8) != 0)
return 0;
if(plen % 8 == 0) {
return 1;
} else {
int i = plen / 8 + 1;
unsigned char mask = (0xFF << (plen % 8)) & 0xFF;
return (a[i] & mask) == (p[i] & mask);
}
}
static void
random_bits(unsigned char *buf, int first, int len)
{
int i;
if(first % 8 != 0) {
unsigned char mask = (0xFF >> (first % 8)) ^ 0xFF;
buf[first / 8] &= mask;
buf[first / 8] |= random() & (0xFF ^ mask);
}
for(i = (first + 7) / 8; i < (first + len) / 8; i++)
buf[i] = random() % 0xFF;
if((first + len) % 8 != 0) {
unsigned char mask = 0xFF >> ((first + len) % 8);
buf[(first + len) / 8] &= mask;
buf[(first + len) / 8] |= random() & (0xFF ^ mask);
}
}
int
random_prefix(const unsigned char *p, int plen, unsigned char *q, int qlen)
{
if(plen < 0 || plen > qlen)
return -1;
memset(q, 0, (qlen + 7) / 8);
memcpy(q, p, (plen + 7) / 8);
random_bits(q, plen, qlen - plen);
return 1;
}
int in_prefix(const unsigned char *a, const unsigned char *p, int plen);
int
random_prefix(const unsigned char *p, int plen, unsigned char *q, int qlen);
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/icmp6.h>
#include "interface.h"
#include "client.h"
#include "lease.h"
#include "flood.h"
#include "util.h"
#include "ra.h"
unsigned char dnsv6[16][16];
int numdnsv6 = 0;
int ra_socket = -1;
int
setup_ra_socket()
{
int s, i, rc, one = 1, ff = 255;
struct icmp6_filter filter;
if(ra_socket >= 0) {
close(ra_socket);
ra_socket = -1;
}
s = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
if(s < 0)
return -1;
rc = setsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ff, sizeof(ff));
if(rc < 0)
goto fail;
rc = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ff, sizeof(ff));
if(rc < 0)
goto fail;
rc = setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
if(rc < 0)
goto fail;
for(i = 0; i < numinterfaces; i++) {
struct ipv6_mreq mreq;
const unsigned char all_routers[16] =
{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02};
if(interfaces[i].ifindex <= 0)
continue;
memset(&mreq, 0, sizeof(mreq));
memcpy(&mreq.ipv6mr_multiaddr, &all_routers, 16);
mreq.ipv6mr_interface = interfaces[i].ifindex;
rc = setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP,
(char*)&mreq, sizeof(mreq));
if(rc < 0)
goto fail;
}
ICMP6_FILTER_SETBLOCKALL(&filter);
ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
rc = setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter));
if(rc < 0)
goto fail;
rc = fcntl(s, F_GETFD, 0);
if(rc < 0)
goto fail;
rc = fcntl(s, F_SETFD, rc | FD_CLOEXEC);
if(rc < 0)
goto fail;
rc = fcntl(s, F_GETFL, 0);
if(rc < 0)
goto fail;
rc = fcntl(s, F_SETFL, (rc | O_NONBLOCK));
if(rc < 0)
goto fail;
ra_socket = s;
return s;
fail:
return -1;
}
#define CHECK(_n) if(buflen < i + (_n)) goto sendit
#define BYTE(_v) buf[i] = (_v); i++
#define BYTES(_v, _len) memcpy(buf + i, (_v), (_len)); i += (_len)
#define SHORT(_v) DO_HTONS(buf + i, (_v)); i += 2
#define LONG(_v) DO_HTONL(buf + i, (_v)); i += 4
static const unsigned char zeroes[8] = {0};
int
send_ra(const unsigned char *prefix, int tm,
const struct sockaddr_in6 *to, struct interface *interface)
{
int buflen = 1024;
unsigned char buf[buflen];
int i = 0;
if(tm > 0xffff)
tm = 0xffff;
CHECK(16);
BYTE(134);
BYTE(0);
SHORT(0);
BYTE(0);
BYTE(0);
SHORT(prefix != NULL ? tm : 0);
LONG(0);
LONG(0);
if(prefix != NULL) {
CHECK(32);
BYTE(3);
BYTE(4);
BYTE(64);
BYTE(0x80 | 0x40);
LONG(tm);
LONG(2 * tm / 3);
LONG(0);
BYTES(prefix, 8);
BYTES(zeroes, 8);
if(numdnsv6 > 0) {
CHECK(8 + numdnsv6 * 16);
BYTE(25);
BYTE(1 + numdnsv6 * 2);
SHORT(0);
LONG(MAX_RTR_ADV_INTERVAL * 3 / 2);
for(int j = 0; j < numdnsv6; j++) {
BYTES(&dnsv6[j], 16);
}
}
}
if(memcmp(interface->mac, zeroes, 6) != 0) {
CHECK(8);
BYTE(1);
BYTE(1);
BYTES(interface->mac, 6);
}
sendit:
debugf("-> RA\n");
return sendto(ra_socket, buf, i, 0, (struct sockaddr*)to, sizeof(*to));
}
int
receive_rs()
{
int buflen = 1500, rc;
unsigned char buf[buflen];
unsigned char *mac;
struct sockaddr_in6 from;
struct interface *interface;
struct iovec iov[1];
struct msghdr msg;
int cmsglen = 100;
char cmsgbuf[100];
struct cmsghdr *cmsg = (struct cmsghdr*)cmsgbuf;
int hoplimit = -1;
int i, doit;
struct datum *lease;
const unsigned char *addr;
struct client *client;
iov[0].iov_base = buf;
iov[0].iov_len = buflen;
memset(&msg, 0, sizeof(msg));
msg.msg_name = &from;
msg.msg_namelen = sizeof(from);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = cmsg;
msg.msg_controllen = cmsglen;
rc = recvmsg(ra_socket, &msg, 0);
if(rc < 0)
return rc;
if(msg.msg_namelen < sizeof(struct sockaddr_in6) ||
from.sin6_family != AF_INET6)
return 0;
cmsg = CMSG_FIRSTHDR(&msg);
while(cmsg != NULL) {
if ((cmsg->cmsg_level == IPPROTO_IPV6) &&
(cmsg->cmsg_type == IPV6_HOPLIMIT)) {
hoplimit = *(unsigned char*)CMSG_DATA(cmsg);
break;
}
}
if(hoplimit != 255)
return 0;
if(rc < 8)
return 0;
if(buf[0] != 133 || buf[1] != 0)
return 0;
if(from.sin6_scope_id == 0)
return 0;
interface = find_interface(from.sin6_scope_id);
if(interface == NULL)
return 0;
mac = NULL;
i = 8;
while(i <= rc - 8) {
if(buf[i] == 1 && buf[i + 1] == 1) {
mac = buf + i + 2;
break;
}
i += buf[i + 1] * 8;
}
if(mac == NULL) {
debugf("No source address option in router solicitation.\n");
return -1;
}
debugf("<- RS %s\n", interface->ifname);
client = update_association(interface, mac, ASSOCIATION_TIME);
if(client == NULL) {
fprintf(stderr, "Failed to create client.\n");
return -1;
}
lease = update_lease(mac, 1, NULL, 3600, &doit);
if(lease == NULL)
return -1;
addr = lease_address(lease, 1);
if(addr == NULL)
return -1;
update_client_route(client, addr, 1);
send_ra(addr, datum_remaining(lease), &from, interface);
return 1;
}
static const unsigned char all_nodes[16] =
{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
int
send_gratuitious_na(struct interface *interface)
{
int buflen = 1024;
unsigned char buf[buflen], myipv6[16];
struct sockaddr_in6 to;
int rc, i = 0;
memset(&to, 0, sizeof(to));
to.sin6_family = AF_INET6;
memcpy(&to.sin6_addr, all_nodes, 16);
to.sin6_scope_id = interface->ifindex;
rc = interface_v6(interface, myipv6);
if(rc < 0)
return rc;
CHECK(24);
BYTE(136);
BYTE(0);
SHORT(0);
BYTE(0x80 | 0x20);
BYTE(0);
SHORT(0);
BYTES(myipv6, 16);
if(memcmp(interface->mac, zeroes, 6) != 0) {
CHECK(8);
BYTE(2);
BYTE(1);
BYTES(interface->mac, 6);
}
sendit:
debugf("-> Neigbour Advertisement\n");
return sendto(ra_socket, buf, i, 0, (struct sockaddr*)&to, sizeof(to));
}
int
ra_setup()
{
return setup_ra_socket();
}
void
ra_cleanup()
{
if(ra_socket < 0)
return;
for(int i = 0; i < numinterfaces; i++) {
struct sockaddr_in6 to;
memset(&to, 0, sizeof(to));
to.sin6_family = AF_INET6;
memcpy(&to.sin6_addr, all_nodes, 16);
to.sin6_scope_id = interfaces[i].ifindex;
send_ra(NULL, 0, &to, &interfaces[i]);
}
close(ra_socket);
ra_socket = -1;
}
/*
Copyright (c) 2015 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#define MAX_RA_DELAY_TIME 500
#define MIN_DELAY_BETWEEN_RAS 3000
#define MAX_RTR_ADV_INTERVAL 600000
#define MIN_RTR_ADV_INTERVAL (33 * MAX_RTR_ADV_INTERVAL / 100)
extern int ra_socket;
extern unsigned char dnsv6[16][16];
extern int numdnsv6;
int ra_setup(void);
void ra_cleanup(void);
int send_ra(const unsigned char *prefix, int tm,
const struct sockaddr_in6 *to, struct interface *interface);
int receive_rs(void);
int send_gratuitious_na(struct interface *interface);
This diff is collapsed.
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#ifdef HAVE_GETRANDOM
#include <sys/random.h>
#endif
#include <stdarg.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <net/if.h>
#include <net/if_arp.h>
#include "util.h"
#ifndef NO_DEBUG
int debug_level = 0;
#endif
int
read_random_bytes(void *buf, int len)
{
int rc;
#ifdef HAVE_GETRANDOM
rc = getrandom(buf, len, 0);
#else
rc = -1;
errno = ENOSYS;
#endif
if(rc < 0 && errno == ENOSYS) {
int fd;
fd = open("/dev/urandom", O_RDONLY);
if(fd < 0)
return -1;
rc = read(fd, buf, len);
close(fd);
}
if(rc < len)
rc = -1;
return rc;
}
int
ts_compare(const struct timespec *s1, const struct timespec *s2)
{
if(s1->tv_sec < s2->tv_sec)
return -1;
else if(s1->tv_sec > s2->tv_sec)
return 1;
else if(s1->tv_nsec < s2->tv_nsec)
return -1;
else if(s1->tv_nsec > s2->tv_nsec)
return 1;
else
return 0;
}
/* {0, 0} represents infinity */
void
ts_min(struct timespec *d, const struct timespec *s)
{
if(s->tv_sec == 0)
return;
if(d->tv_sec == 0 || ts_compare(d, s) > 0) {
*d = *s;
}
}
void
ts_minus(struct timespec *d,
const struct timespec *s1, const struct timespec *s2)
{
if(s1->tv_nsec >= s2->tv_nsec) {
d->tv_nsec = s1->tv_nsec - s2->tv_nsec;
d->tv_sec = s1->tv_sec - s2->tv_sec;
} else {
d->tv_nsec = s1->tv_nsec + 1000000000 - s2->tv_nsec;
d->tv_sec = s1->tv_sec - s2->tv_sec - 1;
}
}
int
ts_minus_msec(const struct timespec *s1, const struct timespec *s2)
{
return (s1->tv_sec - s2->tv_sec) * 1000 +
(s1->tv_nsec - s2->tv_nsec) / 1000000;
}
static void
ts_add_nsec(struct timespec *d, const struct timespec *s, long long nsecs)
{
*d = *s;
while(nsecs + d->tv_nsec > 1000000000) {
d->tv_sec += 1;
nsecs -= 1000000000LL;
}
while(nsecs + d->tv_nsec < 0) {
d->tv_sec -= 1;
nsecs += 1000000000LL;
}
d->tv_nsec += nsecs;
}
static const long long million = 1000000LL;
void
ts_add_msec(struct timespec *d, const struct timespec *s, int msecs)
{
ts_add_nsec(d, s, msecs * million);
}
void
ts_add_random(struct timespec *d, const struct timespec *s, int msecs)
{
ts_add_nsec(d, s, (random() % msecs) * million + random() % million);
}
void
ts_zero(struct timespec *d)
{
d->tv_sec = 0;
d->tv_nsec = 0;
}
const char *
format_32(const unsigned char *data)
{
static char buf[4][16];
static int i = 0;
i = (i + 1) % 4;
snprintf(buf[i], 16, "%02x:%02x:%02x:%02x",
data[0], data[1], data[2], data[3]);
return buf[i];
}
const char *
format_48(const unsigned char *data)
{
static char buf[4][22];
static int i = 0;
i = (i + 1) % 4;
snprintf(buf[i], 22, "%02x:%02x:%02x:%02x:%02x:%02x",
data[0], data[1], data[2], data[3], data[4], data[5]);
return buf[i];
}
const char *
format_64(const unsigned char *data)
{
static char buf[4][28];
static int i = 0;
i = (i + 1) % 4;
snprintf(buf[i], 28, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
data[0], data[1], data[2], data[3],
data[4], data[5], data[6], data[7]);
return buf[i];
}
void
do_debugf(int level, const char *format, ...)
{
va_list args;
va_start(args, format);
if(debug_level >= level) {
vfprintf(stderr, format, args);
fflush(stderr);
}
va_end(args);
}
int
parse_address(const char *string, unsigned char *p_return)
{
char buf[INET6_ADDRSTRLEN];
int rc;
struct in_addr ina;
struct in6_addr ina6;
strncpy(buf, string, INET6_ADDRSTRLEN);
buf[INET6_ADDRSTRLEN - 1] = '\0';
rc = inet_pton(AF_INET, buf, &ina);
if(rc > 0) {
memcpy(p_return, &ina, 4);
return 4;
}
rc = inet_pton(AF_INET6, buf, &ina6);
if(rc > 0) {
memcpy(p_return, &ina6, 16);
return 6;
}
return -1;
}
int
parse_prefix(const char *string, unsigned char *p_return, int *plen_return)
{
char buf[INET6_ADDRSTRLEN];
int rc, plen;
char *slash;
struct in_addr ina;
struct in6_addr ina6;
strncpy(buf, string, INET6_ADDRSTRLEN);
buf[INET6_ADDRSTRLEN - 1] = '\0';
slash = strchr(buf, '/');
if(slash == NULL) {
return -1;
} else {
char *end;
plen = strtol(slash + 1, &end, 0);
if(*end != '\0' || plen < 0 || plen > 128)
return -1;
*slash = '\0';
}
rc = inet_pton(AF_INET, buf, &ina);
if(rc > 0) {
memcpy(p_return, &ina, 4);
*plen_return = plen;
return 4;
}
rc = inet_pton(AF_INET6, buf, &ina6);
if(rc > 0) {
memcpy(p_return, &ina6, 16);
*plen_return = plen;
return 6;
}
return -1;
}
int parse_addrport(const char *string, unsigned char *a_return,
unsigned short *port_return)
{
char buf[INET6_ADDRSTRLEN];
int rc, port;
char *colon;
struct in_addr ina;
struct in6_addr ina6;
strncpy(buf, string, INET6_ADDRSTRLEN);
buf[INET6_ADDRSTRLEN - 1] = '\0';
colon = strchr(buf, ':');
if(colon == NULL) {
return -1;
} else {
char *end;
port = strtol(colon + 1, &end, 0);
if(*end != '\0' || port <= 0 || port > 0xFFFF)
return -1;
*colon = '\0';
}
rc = inet_pton(AF_INET, buf, &ina);
if(rc > 0) {
memcpy(a_return, &ina, 4);
*port_return = port;
return 4;
}
rc = inet_pton(AF_INET6, buf, &ina6);
if(rc > 0) {
memcpy(a_return, &ina6, 16);
*port_return = port;
return 6;
}
return -1;
}
static const unsigned char zeroes[6];
int
if_macaddr(char *ifname, int ifindex, unsigned char *mac_return)
{
int s, rc;
struct ifreq ifr;
unsigned char *mac;
s = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
if(s < 0) return -1;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
rc = ioctl(s, SIOCGIFHWADDR, &ifr);
if(rc < 0) {
int saved_errno = errno;
close(s);
errno = saved_errno;
return -1;
}
close(s);
if(ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
debugf("Unknown hardware type %d.\n", ifr.ifr_hwaddr.sa_family);
errno = ENOENT;
return -1;
}
mac = (unsigned char *)ifr.ifr_hwaddr.sa_data;
if(memcmp(mac, zeroes, 6) == 0) {
errno = ENOENT;
return -1;
}
memcpy(mac_return, mac, 6);
return 1;
}
/*
Copyright (c) 2015 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifdef NO_DEBUG
#define debugf(...) do {} while(0)
#else
extern int debug_level;
#define debugf(...) \
do { \
if(debug_level >= 2) do_debugf(2, __VA_ARGS__); \
} while(0)
#endif
#define DO_NTOHS(_d, _s) \
do { unsigned short _dd; \
memcpy(&(_dd), (_s), 2); \
_d = ntohs(_dd); } while(0)
#define DO_NTOHL(_d, _s) \
do { unsigned int _dd; \
memcpy(&(_dd), (_s), 4); \
_d = ntohl(_dd); } while(0)
#define DO_HTONS(_d, _s) \
do { unsigned short _dd; \
_dd = htons(_s); \
memcpy((_d), &(_dd), 2); } while(0)
#define DO_HTONL(_d, _s) \
do { unsigned _dd; \
_dd = htonl(_s); \
memcpy((_d), &(_dd), 4); } while(0)
#ifdef HAVE_VALGRIND
#include <valgrind/memcheck.h>
#define MEM_UNDEFINED VALGRIND_MAKE_MEM_UNDEFINED
#else
#define MEM_UNDEFINED(_a, _l) do {} while(0)
#endif
static inline int
min(int x, int y)
{
return x > y ? y : x;
}
static inline int
max(int x, int y)
{
return x > y ? x : y;
}
int read_random_bytes(void *buf, int len);
int ts_compare(const struct timespec *s1, const struct timespec *s2);
void ts_min(struct timespec *d, const struct timespec *s);
void ts_minus(struct timespec *d,
const struct timespec *s1, const struct timespec *s2);
int ts_minus_msec(const struct timespec *s1, const struct timespec *s2);
void ts_min_sec(struct timespec *d, int secs);
void ts_add_msec(struct timespec *d, const struct timespec *s, int msecs);
void ts_add_random(struct timespec *d, const struct timespec *s, int msecs);
void ts_zero(struct timespec *d);
const char *format_32(const unsigned char *data);
const char *format_48(const unsigned char *data);
const char *format_64(const unsigned char *data);
void do_debugf(int level, const char *format, ...)
#ifdef __GNUC__
__attribute__((format (printf, 2, 3))) __attribute__((cold))
#endif
;
int parse_address(const char *string, unsigned char *p_return);
int parse_prefix(const char *string, unsigned char *p_return, int *plen_return);
int parse_addrport(const char *string, unsigned char *a_return,
unsigned short *port_return);
int if_macaddr(char *ifname, int ifindex, unsigned char *mac_return);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment