Blame raw.c

Packit 9c3e7e
/**
Packit 9c3e7e
 * @file raw.c
Packit 9c3e7e
 * @note Copyright (C) 2012 Richard Cochran <richardcochran@gmail.com>
Packit 9c3e7e
 *
Packit 9c3e7e
 * This program is free software; you can redistribute it and/or modify
Packit 9c3e7e
 * it under the terms of the GNU General Public License as published by
Packit 9c3e7e
 * the Free Software Foundation; either version 2 of the License, or
Packit 9c3e7e
 * (at your option) any later version.
Packit 9c3e7e
 *
Packit 9c3e7e
 * This program is distributed in the hope that it will be useful,
Packit 9c3e7e
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 9c3e7e
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 9c3e7e
 * GNU General Public License for more details.
Packit 9c3e7e
 *
Packit 9c3e7e
 * You should have received a copy of the GNU General Public License along
Packit 9c3e7e
 * with this program; if not, write to the Free Software Foundation, Inc.,
Packit 9c3e7e
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Packit 9c3e7e
 */
Packit 9c3e7e
#include <errno.h>
Packit 9c3e7e
#include <fcntl.h>
Packit 9c3e7e
#include <linux/filter.h>
Packit 9c3e7e
#include <linux/if_ether.h>
Packit 9c3e7e
#include <net/if.h>
Packit 9c3e7e
#include <netinet/in.h>
Packit 9c3e7e
#include <netpacket/packet.h>
Packit 9c3e7e
#include <stdio.h>
Packit 9c3e7e
#include <stdlib.h>
Packit 9c3e7e
#include <string.h>
Packit 9c3e7e
#include <sys/ioctl.h>
Packit 9c3e7e
#include <sys/socket.h>
Packit 9c3e7e
#include <sys/types.h>
Packit 9c3e7e
#include <unistd.h>
Packit 9c3e7e
Packit 9c3e7e
#include <linux/errqueue.h>
Packit 9c3e7e
#include <linux/net_tstamp.h>
Packit 9c3e7e
#include <linux/sockios.h>
Packit 9c3e7e
Packit 9c3e7e
#include "address.h"
Packit 9c3e7e
#include "config.h"
Packit 9c3e7e
#include "contain.h"
Packit 9c3e7e
#include "ether.h"
Packit 9c3e7e
#include "print.h"
Packit 9c3e7e
#include "raw.h"
Packit 9c3e7e
#include "sk.h"
Packit 9c3e7e
#include "transport_private.h"
Packit 9c3e7e
#include "util.h"
Packit 9c3e7e
Packit 9c3e7e
struct raw {
Packit 9c3e7e
	struct transport t;
Packit 9c3e7e
	struct address src_addr;
Packit 9c3e7e
	struct address ptp_addr;
Packit 9c3e7e
	struct address p2p_addr;
Packit 9c3e7e
	int vlan;
Packit 9c3e7e
};
Packit 9c3e7e
Packit 9c3e7e
#define OP_AND  (BPF_ALU | BPF_AND | BPF_K)
Packit 9c3e7e
#define OP_JEQ  (BPF_JMP | BPF_JEQ | BPF_K)
Packit 9c3e7e
#define OP_JUN  (BPF_JMP | BPF_JA)
Packit 9c3e7e
#define OP_LDB  (BPF_LD  | BPF_B   | BPF_ABS)
Packit 9c3e7e
#define OP_LDH  (BPF_LD  | BPF_H   | BPF_ABS)
Packit 9c3e7e
#define OP_RETK (BPF_RET | BPF_K)
Packit 9c3e7e
Packit 9c3e7e
#define PTP_GEN_BIT 0x08 /* indicates general message, if set in message type */
Packit 9c3e7e
Packit 9c3e7e
#define N_RAW_FILTER    12
Packit 9c3e7e
#define RAW_FILTER_TEST 9
Packit 9c3e7e
Packit 9c3e7e
static struct sock_filter raw_filter[N_RAW_FILTER] = {
Packit 9c3e7e
	{OP_LDH,  0, 0, OFF_ETYPE   },
Packit 9c3e7e
	{OP_JEQ,  0, 4, ETH_P_8021Q          }, /*f goto non-vlan block*/
Packit 9c3e7e
	{OP_LDH,  0, 0, OFF_ETYPE + 4        },
Packit 9c3e7e
	{OP_JEQ,  0, 7, ETH_P_1588           }, /*f goto reject*/
Packit 9c3e7e
	{OP_LDB,  0, 0, ETH_HLEN + VLAN_HLEN },
Packit 9c3e7e
	{OP_JUN,  0, 0, 2                    }, /*goto test general bit*/
Packit 9c3e7e
	{OP_JEQ,  0, 4, ETH_P_1588  }, /*f goto reject*/
Packit 9c3e7e
	{OP_LDB,  0, 0, ETH_HLEN    },
Packit 9c3e7e
	{OP_AND,  0, 0, PTP_GEN_BIT }, /*test general bit*/
Packit 9c3e7e
	{OP_JEQ,  0, 1, 0           }, /*0,1=accept event; 1,0=accept general*/
Packit 9c3e7e
	{OP_RETK, 0, 0, 1500        }, /*accept*/
Packit 9c3e7e
	{OP_RETK, 0, 0, 0           }, /*reject*/
Packit 9c3e7e
};
Packit 9c3e7e
Packit 9c3e7e
static int raw_configure(int fd, int event, int index,
Packit 9c3e7e
			 unsigned char *addr1, unsigned char *addr2, int enable)
Packit 9c3e7e
{
Packit 9c3e7e
	int err1, err2, filter_test, option;
Packit 9c3e7e
	struct packet_mreq mreq;
Packit 9c3e7e
	struct sock_fprog prg = { N_RAW_FILTER, raw_filter };
Packit 9c3e7e
Packit 9c3e7e
	filter_test = RAW_FILTER_TEST;
Packit 9c3e7e
	if (event) {
Packit 9c3e7e
		raw_filter[filter_test].jt = 0;
Packit 9c3e7e
		raw_filter[filter_test].jf = 1;
Packit 9c3e7e
	} else {
Packit 9c3e7e
		raw_filter[filter_test].jt = 1;
Packit 9c3e7e
		raw_filter[filter_test].jf = 0;
Packit 9c3e7e
	}
Packit 9c3e7e
Packit 9c3e7e
	if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &prg, sizeof(prg))) {
Packit 9c3e7e
		pr_err("setsockopt SO_ATTACH_FILTER failed: %m");
Packit 9c3e7e
		return -1;
Packit 9c3e7e
	}
Packit 9c3e7e
Packit 9c3e7e
	option = enable ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP;
Packit 9c3e7e
Packit 9c3e7e
	memset(&mreq, 0, sizeof(mreq));
Packit 9c3e7e
	mreq.mr_ifindex = index;
Packit 9c3e7e
	mreq.mr_type = PACKET_MR_MULTICAST;
Packit 9c3e7e
	mreq.mr_alen = MAC_LEN;
Packit 9c3e7e
	memcpy(mreq.mr_address, addr1, MAC_LEN);
Packit 9c3e7e
Packit 9c3e7e
	err1 = setsockopt(fd, SOL_PACKET, option, &mreq, sizeof(mreq));
Packit 9c3e7e
	if (err1)
Packit 9c3e7e
		pr_warning("setsockopt PACKET_MR_MULTICAST failed: %m");
Packit 9c3e7e
Packit 9c3e7e
	memcpy(mreq.mr_address, addr2, MAC_LEN);
Packit 9c3e7e
Packit 9c3e7e
	err2 = setsockopt(fd, SOL_PACKET, option, &mreq, sizeof(mreq));
Packit 9c3e7e
	if (err2)
Packit 9c3e7e
		pr_warning("setsockopt PACKET_MR_MULTICAST failed: %m");
Packit 9c3e7e
Packit 9c3e7e
	if (!err1 && !err2)
Packit 9c3e7e
		return 0;
Packit 9c3e7e
Packit 9c3e7e
	mreq.mr_ifindex = index;
Packit 9c3e7e
	mreq.mr_type = PACKET_MR_ALLMULTI;
Packit 9c3e7e
	mreq.mr_alen = 0;
Packit 9c3e7e
	if (!setsockopt(fd, SOL_PACKET, option, &mreq, sizeof(mreq))) {
Packit 9c3e7e
		return 0;
Packit 9c3e7e
	}
Packit 9c3e7e
	pr_warning("setsockopt PACKET_MR_ALLMULTI failed: %m");
Packit 9c3e7e
Packit 9c3e7e
	mreq.mr_ifindex = index;
Packit 9c3e7e
	mreq.mr_type = PACKET_MR_PROMISC;
Packit 9c3e7e
	mreq.mr_alen = 0;
Packit 9c3e7e
	if (!setsockopt(fd, SOL_PACKET, option, &mreq, sizeof(mreq))) {
Packit 9c3e7e
		return 0;
Packit 9c3e7e
	}
Packit 9c3e7e
	pr_warning("setsockopt PACKET_MR_PROMISC failed: %m");
Packit 9c3e7e
Packit 9c3e7e
	pr_err("all socket options failed");
Packit 9c3e7e
	return -1;
Packit 9c3e7e
}
Packit 9c3e7e
Packit 9c3e7e
static int raw_close(struct transport *t, struct fdarray *fda)
Packit 9c3e7e
{
Packit 9c3e7e
	close(fda->fd[0]);
Packit 9c3e7e
	close(fda->fd[1]);
Packit 9c3e7e
	return 0;
Packit 9c3e7e
}
Packit 9c3e7e
Packit 9c3e7e
static int open_socket(const char *name, int event, unsigned char *ptp_dst_mac,
Packit 9c3e7e
		       unsigned char *p2p_dst_mac)
Packit 9c3e7e
{
Packit 9c3e7e
	struct sockaddr_ll addr;
Packit 9c3e7e
	int fd, index;
Packit 9c3e7e
Packit 9c3e7e
	fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
Packit 9c3e7e
	if (fd < 0) {
Packit 9c3e7e
		pr_err("socket failed: %m");
Packit 9c3e7e
		goto no_socket;
Packit 9c3e7e
	}
Packit 9c3e7e
	index = sk_interface_index(fd, name);
Packit 9c3e7e
	if (index < 0)
Packit 9c3e7e
		goto no_option;
Packit 9c3e7e
Packit 9c3e7e
	memset(&addr, 0, sizeof(addr));
Packit 9c3e7e
	addr.sll_ifindex = index;
Packit 9c3e7e
	addr.sll_family = AF_PACKET;
Packit 9c3e7e
	addr.sll_protocol = htons(ETH_P_ALL);
Packit 9c3e7e
	if (bind(fd, (struct sockaddr *) &addr, sizeof(addr))) {
Packit 9c3e7e
		pr_err("bind failed: %m");
Packit 9c3e7e
		goto no_option;
Packit 9c3e7e
	}
Packit 9c3e7e
	if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, name, strlen(name))) {
Packit 9c3e7e
		pr_err("setsockopt SO_BINDTODEVICE failed: %m");
Packit 9c3e7e
		goto no_option;
Packit 9c3e7e
	}
Packit 9c3e7e
	if (raw_configure(fd, event, index, ptp_dst_mac, p2p_dst_mac, 1))
Packit 9c3e7e
		goto no_option;
Packit 9c3e7e
Packit 9c3e7e
	return fd;
Packit 9c3e7e
no_option:
Packit 9c3e7e
	close(fd);
Packit 9c3e7e
no_socket:
Packit 9c3e7e
	return -1;
Packit 9c3e7e
}
Packit 9c3e7e
Packit 9c3e7e
static void mac_to_addr(struct address *addr, void *mac)
Packit 9c3e7e
{
Packit 9c3e7e
	addr->sll.sll_family = AF_PACKET;
Packit 9c3e7e
	addr->sll.sll_halen = MAC_LEN;
Packit 9c3e7e
	memcpy(addr->sll.sll_addr, mac, MAC_LEN);
Packit 9c3e7e
	addr->len = sizeof(addr->sll);
Packit 9c3e7e
}
Packit 9c3e7e
Packit 9c3e7e
static void addr_to_mac(void *mac, struct address *addr)
Packit 9c3e7e
{
Packit 9c3e7e
	memcpy(mac, &addr->sll.sll_addr, MAC_LEN);
Packit 9c3e7e
}
Packit 9c3e7e
Packit 9c3e7e
static int raw_open(struct transport *t, struct interface *iface,
Packit 9c3e7e
		    struct fdarray *fda, enum timestamp_type ts_type)
Packit 9c3e7e
{
Packit 9c3e7e
	struct raw *raw = container_of(t, struct raw, t);
Packit 9c3e7e
	unsigned char ptp_dst_mac[MAC_LEN];
Packit 9c3e7e
	unsigned char p2p_dst_mac[MAC_LEN];
Packit 9c3e7e
	int efd, gfd;
Packit 9c3e7e
	char *str, *name;
Packit 9c3e7e
Packit 9c3e7e
	name = iface->ts_label;
Packit 9c3e7e
	str = config_get_string(t->cfg, name, "ptp_dst_mac");
Packit 9c3e7e
	if (str2mac(str, ptp_dst_mac)) {
Packit 9c3e7e
		pr_err("invalid ptp_dst_mac %s", str);
Packit 9c3e7e
		return -1;
Packit 9c3e7e
	}
Packit 9c3e7e
	str = config_get_string(t->cfg, name, "p2p_dst_mac");
Packit 9c3e7e
	if (str2mac(str, p2p_dst_mac)) {
Packit 9c3e7e
		pr_err("invalid p2p_dst_mac %s", str);
Packit 9c3e7e
		return -1;
Packit 9c3e7e
	}
Packit 9c3e7e
	mac_to_addr(&raw->ptp_addr, ptp_dst_mac);
Packit 9c3e7e
	mac_to_addr(&raw->p2p_addr, p2p_dst_mac);
Packit 9c3e7e
Packit 9c3e7e
	if (sk_interface_macaddr(name, &raw->src_addr))
Packit 9c3e7e
		goto no_mac;
Packit 9c3e7e
Packit 9c3e7e
	efd = open_socket(name, 1, ptp_dst_mac, p2p_dst_mac);
Packit 9c3e7e
	if (efd < 0)
Packit 9c3e7e
		goto no_event;
Packit 9c3e7e
Packit 9c3e7e
	gfd = open_socket(name, 0, ptp_dst_mac, p2p_dst_mac);
Packit 9c3e7e
	if (gfd < 0)
Packit 9c3e7e
		goto no_general;
Packit 9c3e7e
Packit 9c3e7e
	if (sk_timestamping_init(efd, name, ts_type, TRANS_IEEE_802_3))
Packit 9c3e7e
		goto no_timestamping;
Packit 9c3e7e
Packit 9c3e7e
	if (sk_general_init(gfd))
Packit 9c3e7e
		goto no_timestamping;
Packit 9c3e7e
Packit 9c3e7e
	fda->fd[FD_EVENT] = efd;
Packit 9c3e7e
	fda->fd[FD_GENERAL] = gfd;
Packit 9c3e7e
	return 0;
Packit 9c3e7e
Packit 9c3e7e
no_timestamping:
Packit 9c3e7e
	close(gfd);
Packit 9c3e7e
no_general:
Packit 9c3e7e
	close(efd);
Packit 9c3e7e
no_event:
Packit 9c3e7e
no_mac:
Packit 9c3e7e
	return -1;
Packit 9c3e7e
}
Packit 9c3e7e
Packit 9c3e7e
static int raw_recv(struct transport *t, int fd, void *buf, int buflen,
Packit 9c3e7e
		    struct address *addr, struct hw_timestamp *hwts)
Packit 9c3e7e
{
Packit 9c3e7e
	int cnt, hlen;
Packit 9c3e7e
	unsigned char *ptr = buf;
Packit 9c3e7e
	struct eth_hdr *hdr;
Packit 9c3e7e
	struct raw *raw = container_of(t, struct raw, t);
Packit 9c3e7e
Packit 9c3e7e
	if (raw->vlan) {
Packit 9c3e7e
		hlen = sizeof(struct vlan_hdr);
Packit 9c3e7e
	} else {
Packit 9c3e7e
		hlen = sizeof(struct eth_hdr);
Packit 9c3e7e
	}
Packit 9c3e7e
	ptr    -= hlen;
Packit 9c3e7e
	buflen += hlen;
Packit 9c3e7e
	hdr = (struct eth_hdr *) ptr;
Packit 9c3e7e
Packit 9c3e7e
	cnt = sk_receive(fd, ptr, buflen, addr, hwts, 0);
Packit 9c3e7e
Packit 9c3e7e
	if (cnt >= 0)
Packit 9c3e7e
		cnt -= hlen;
Packit 9c3e7e
	if (cnt < 0)
Packit 9c3e7e
		return cnt;
Packit 9c3e7e
Packit 9c3e7e
	if (raw->vlan) {
Packit 9c3e7e
		if (ETH_P_1588 == ntohs(hdr->type)) {
Packit 9c3e7e
			pr_notice("raw: disabling VLAN mode");
Packit 9c3e7e
			raw->vlan = 0;
Packit 9c3e7e
		}
Packit 9c3e7e
	} else {
Packit 9c3e7e
		if (ETH_P_8021Q == ntohs(hdr->type)) {
Packit 9c3e7e
			pr_notice("raw: switching to VLAN mode");
Packit 9c3e7e
			raw->vlan = 1;
Packit 9c3e7e
		}
Packit 9c3e7e
	}
Packit 9c3e7e
	return cnt;
Packit 9c3e7e
}
Packit 9c3e7e
Packit 9c3e7e
static int raw_send(struct transport *t, struct fdarray *fda,
Packit 9c3e7e
		    enum transport_event event, int peer, void *buf, int len,
Packit 9c3e7e
		    struct address *addr, struct hw_timestamp *hwts)
Packit 9c3e7e
{
Packit 9c3e7e
	struct raw *raw = container_of(t, struct raw, t);
Packit 9c3e7e
	ssize_t cnt;
Packit 9c3e7e
	unsigned char pkt[1600], *ptr = buf;
Packit 9c3e7e
	struct eth_hdr *hdr;
Packit 9c3e7e
	int fd = -1;
Packit 9c3e7e
Packit 9c3e7e
	switch (event) {
Packit 9c3e7e
	case TRANS_GENERAL:
Packit 9c3e7e
		fd = fda->fd[FD_GENERAL];
Packit 9c3e7e
		break;
Packit 9c3e7e
	case TRANS_EVENT:
Packit 9c3e7e
	case TRANS_ONESTEP:
Packit 9c3e7e
	case TRANS_P2P1STEP:
Packit 9c3e7e
	case TRANS_DEFER_EVENT:
Packit 9c3e7e
		fd = fda->fd[FD_EVENT];
Packit 9c3e7e
		break;
Packit 9c3e7e
	}
Packit 9c3e7e
Packit 9c3e7e
	ptr -= sizeof(*hdr);
Packit 9c3e7e
	len += sizeof(*hdr);
Packit 9c3e7e
Packit 9c3e7e
	if (!addr)
Packit 9c3e7e
		addr = peer ? &raw->p2p_addr : &raw->ptp_addr;
Packit 9c3e7e
Packit 9c3e7e
	hdr = (struct eth_hdr *) ptr;
Packit 9c3e7e
	addr_to_mac(&hdr->dst, addr);
Packit 9c3e7e
	addr_to_mac(&hdr->src, &raw->src_addr);
Packit 9c3e7e
Packit 9c3e7e
	hdr->type = htons(ETH_P_1588);
Packit 9c3e7e
Packit 9c3e7e
	cnt = send(fd, ptr, len, 0);
Packit 9c3e7e
	if (cnt < 1) {
Packit 9c3e7e
		pr_err("send failed: %d %m", errno);
Packit 9c3e7e
		return cnt;
Packit 9c3e7e
	}
Packit 9c3e7e
	/*
Packit 9c3e7e
	 * Get the time stamp right away.
Packit 9c3e7e
	 */
Packit 9c3e7e
	return event == TRANS_EVENT ? sk_receive(fd, pkt, len, NULL, hwts, MSG_ERRQUEUE) : cnt;
Packit 9c3e7e
}
Packit 9c3e7e
Packit 9c3e7e
static void raw_release(struct transport *t)
Packit 9c3e7e
{
Packit 9c3e7e
	struct raw *raw = container_of(t, struct raw, t);
Packit 9c3e7e
	free(raw);
Packit 9c3e7e
}
Packit 9c3e7e
Packit 9c3e7e
static int raw_physical_addr(struct transport *t, uint8_t *addr)
Packit 9c3e7e
{
Packit 9c3e7e
	struct raw *raw = container_of(t, struct raw, t);
Packit 9c3e7e
	addr_to_mac(addr, &raw->src_addr);
Packit 9c3e7e
	return MAC_LEN;
Packit 9c3e7e
}
Packit 9c3e7e
Packit 9c3e7e
static int raw_protocol_addr(struct transport *t, uint8_t *addr)
Packit 9c3e7e
{
Packit 9c3e7e
	struct raw *raw = container_of(t, struct raw, t);
Packit 9c3e7e
	addr_to_mac(addr, &raw->src_addr);
Packit 9c3e7e
	return MAC_LEN;
Packit 9c3e7e
}
Packit 9c3e7e
Packit 9c3e7e
struct transport *raw_transport_create(void)
Packit 9c3e7e
{
Packit 9c3e7e
	struct raw *raw;
Packit 9c3e7e
	raw = calloc(1, sizeof(*raw));
Packit 9c3e7e
	if (!raw)
Packit 9c3e7e
		return NULL;
Packit 9c3e7e
	raw->t.close   = raw_close;
Packit 9c3e7e
	raw->t.open    = raw_open;
Packit 9c3e7e
	raw->t.recv    = raw_recv;
Packit 9c3e7e
	raw->t.send    = raw_send;
Packit 9c3e7e
	raw->t.release = raw_release;
Packit 9c3e7e
	raw->t.physical_addr = raw_physical_addr;
Packit 9c3e7e
	raw->t.protocol_addr = raw_protocol_addr;
Packit 9c3e7e
	return &raw->t;
Packit 9c3e7e
}