Blame teamd/teamd_common.c

Packit cac203
/*
Packit cac203
 *   teamd_common.c - Common teamd functions
Packit cac203
 *   Copyright (C) 2012-2015 Jiri Pirko <jiri@resnulli.us>
Packit cac203
 *
Packit cac203
 *   This library is free software; you can redistribute it and/or
Packit cac203
 *   modify it under the terms of the GNU Lesser General Public
Packit cac203
 *   License as published by the Free Software Foundation; either
Packit cac203
 *   version 2.1 of the License, or (at your option) any later version.
Packit cac203
 *
Packit cac203
 *   This library is distributed in the hope that it will be useful,
Packit cac203
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit cac203
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit cac203
 *   Lesser General Public License for more details.
Packit cac203
 *
Packit cac203
 *   You should have received a copy of the GNU Lesser General Public
Packit cac203
 *   License along with this library; if not, write to the Free Software
Packit cac203
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit cac203
 */
Packit cac203
Packit cac203
#include <string.h>
Packit cac203
#include <unistd.h>
Packit cac203
#include <errno.h>
Packit cac203
#include <sys/socket.h>
Packit cac203
#include <sys/types.h>
Packit cac203
#include <pwd.h>
Packit cac203
#include <sys/stat.h>
Packit cac203
#include <linux/if_packet.h>
Packit cac203
#include <linux/filter.h>
Packit cac203
#include <private/misc.h>
Packit cac203
Packit cac203
#include "teamd.h"
Packit cac203
Packit cac203
static struct sock_filter bad_flt[] = {
Packit cac203
	BPF_STMT(BPF_LD + BPF_B + BPF_ABS, -1),
Packit cac203
	BPF_STMT(BPF_RET + BPF_K, 0),
Packit cac203
};
Packit cac203
Packit cac203
static const struct sock_fprog bad_fprog = {
Packit cac203
	.len = ARRAY_SIZE(bad_flt),
Packit cac203
	.filter = bad_flt,
Packit cac203
};
Packit cac203
Packit cac203
static int attach_filter(int sock, const struct sock_fprog *pref_fprog,
Packit cac203
			 const struct sock_fprog *alt_fprog)
Packit cac203
{
Packit cac203
	int ret;
Packit cac203
	const struct sock_fprog *fprog;
Packit cac203
Packit cac203
	if (!pref_fprog)
Packit cac203
		return 0;
Packit cac203
Packit cac203
	/* Now we are in tough situation. Older kernels (<3.8) does not
Packit cac203
	 * support SKF_AD_VLAN_TAG_PRESENT and SKF_AD_VLAN_TAG. But the kernel
Packit cac203
	 * check if these are supported was added after that:
Packit cac203
	 * aa1113d9f85da59dcbdd32aeb5d71da566e46def
Packit cac203
	 * But it was added close enough. So try to attach obviously bad
Packit cac203
	 * filter and assume that is it does not fail, kernel does not support
Packit cac203
	 * accessing skb->vlan_tci getting and use alternative filter instead.
Packit cac203
	 */
Packit cac203
Packit cac203
	ret = setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER,
Packit cac203
			 &bad_fprog, sizeof(bad_fprog));
Packit cac203
	if (ret == -1) {
Packit cac203
		if (errno != EINVAL)
Packit cac203
			return -errno;
Packit cac203
		fprog = pref_fprog;
Packit cac203
	}
Packit cac203
	else if (alt_fprog) {
Packit cac203
		teamd_log_warn("Kernel does not support accessing skb->vlan_tci from BPF,\n"
Packit cac203
			       "falling back to alternative filter. Expect vlan-tagged ARPs\n"
Packit cac203
			       "to be accounted on non-tagged link monitor and vice versa.");
Packit cac203
		fprog = alt_fprog;
Packit cac203
	} else {
Packit cac203
		return 0;
Packit cac203
	}
Packit cac203
Packit cac203
	ret = setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER,
Packit cac203
			 fprog, sizeof(*fprog));
Packit cac203
	if (ret == -1)
Packit cac203
		return -errno;
Packit cac203
	return 0;
Packit cac203
}
Packit cac203
Packit cac203
int teamd_packet_sock_open_type(int type, int *sock_p, const uint32_t ifindex,
Packit cac203
				const unsigned short family,
Packit cac203
				const struct sock_fprog *fprog,
Packit cac203
				const struct sock_fprog *alt_fprog)
Packit cac203
{
Packit cac203
	struct sockaddr_ll ll_my;
Packit cac203
	int sock;
Packit cac203
	int ret;
Packit cac203
	int err;
Packit cac203
Packit cac203
	sock = socket(PF_PACKET, type, 0);
Packit cac203
	if (sock == -1) {
Packit cac203
		teamd_log_err("Failed to create packet socket.");
Packit cac203
		return -errno;
Packit cac203
	}
Packit cac203
Packit cac203
	err = attach_filter(sock, fprog, alt_fprog);
Packit cac203
	if (err) {
Packit cac203
		teamd_log_err("Failed to attach filter.");
Packit cac203
		goto close_sock;
Packit cac203
	}
Packit cac203
Packit cac203
	memset(&ll_my, 0, sizeof(ll_my));
Packit cac203
	ll_my.sll_family = AF_PACKET;
Packit cac203
	ll_my.sll_ifindex = ifindex;
Packit cac203
	ll_my.sll_protocol = family;
Packit cac203
	ret = bind(sock, (struct sockaddr *) &ll_my, sizeof(ll_my));
Packit cac203
	if (ret == -1) {
Packit cac203
		teamd_log_err("Failed to bind socket.");
Packit cac203
		err = -errno;
Packit cac203
		goto close_sock;
Packit cac203
	}
Packit cac203
Packit cac203
	*sock_p = sock;
Packit cac203
	return 0;
Packit cac203
close_sock:
Packit cac203
	close(sock);
Packit cac203
	return err;
Packit cac203
}
Packit cac203
Packit cac203
int teamd_packet_sock_open(int *sock_p, const uint32_t ifindex,
Packit cac203
			   const unsigned short family,
Packit cac203
			   const struct sock_fprog *fprog,
Packit cac203
			   const struct sock_fprog *alt_fprog)
Packit cac203
{
Packit cac203
	return teamd_packet_sock_open_type(SOCK_DGRAM, sock_p, ifindex, family,
Packit cac203
					   fprog, alt_fprog);
Packit cac203
}
Packit cac203
Packit cac203
int teamd_getsockname_hwaddr(int sock, struct sockaddr_ll *addr,
Packit cac203
			     size_t expected_len)
Packit cac203
{
Packit cac203
	socklen_t addr_len;
Packit cac203
	int ret;
Packit cac203
Packit cac203
	addr_len = sizeof(*addr);
Packit cac203
	ret = getsockname(sock, (struct sockaddr *) addr, &addr_len);
Packit cac203
	if (ret == -1) {
Packit cac203
		teamd_log_err("Failed to getsockname.");
Packit cac203
		return -errno;
Packit cac203
	}
Packit cac203
	if (expected_len && addr->sll_halen != expected_len) {
Packit cac203
		teamd_log_err("Unexpected length of hw address.");
Packit cac203
		return -ENOTSUP;
Packit cac203
	}
Packit cac203
	return 0;
Packit cac203
}
Packit cac203
Packit cac203
int teamd_sendto(int sockfd, const void *buf, size_t len, int flags,
Packit cac203
		 const struct sockaddr *dest_addr, socklen_t addrlen)
Packit cac203
{
Packit cac203
	ssize_t ret;
Packit cac203
Packit cac203
resend:
Packit cac203
	ret = sendto(sockfd, buf, len, flags, dest_addr, addrlen);
Packit cac203
	if (ret == -1) {
Packit cac203
		switch(errno) {
Packit cac203
		case EINTR:
Packit cac203
			goto resend;
Packit cac203
		case ENETDOWN:
Packit cac203
		case ENETUNREACH:
Packit cac203
		case EADDRNOTAVAIL:
Packit cac203
		case ENXIO:
Packit cac203
			return 0;
Packit cac203
		default:
Packit cac203
			teamd_log_err("sendto failed.");
Packit cac203
			return -errno;
Packit cac203
		}
Packit cac203
	}
Packit cac203
	return 0;
Packit cac203
}
Packit cac203
Packit cac203
int teamd_send(int sockfd, const void *buf, size_t len, int flags)
Packit cac203
{
Packit cac203
	ssize_t ret;
Packit cac203
Packit cac203
resend:
Packit cac203
	ret = send(sockfd, buf, len, flags);
Packit cac203
	if (ret == -1) {
Packit cac203
		switch(errno) {
Packit cac203
		case EINTR:
Packit cac203
			goto resend;
Packit cac203
		case ENETDOWN:
Packit cac203
		case ENETUNREACH:
Packit cac203
		case EADDRNOTAVAIL:
Packit cac203
		case ENXIO:
Packit cac203
			return 0;
Packit cac203
		default:
Packit cac203
			teamd_log_err("send failed.");
Packit cac203
			return -errno;
Packit cac203
		}
Packit cac203
	}
Packit cac203
	return 0;
Packit cac203
}
Packit cac203
Packit cac203
int teamd_recvfrom(int sockfd, void *buf, size_t len, int flags,
Packit cac203
		   struct sockaddr *src_addr, socklen_t addrlen)
Packit cac203
{
Packit cac203
	size_t ret;
Packit cac203
	socklen_t tmp_addrlen = addrlen;
Packit cac203
Packit cac203
rerecv:
Packit cac203
	ret = recvfrom(sockfd, buf, len, flags, src_addr, &tmp_addrlen);
Packit cac203
	if (ret == -1) {
Packit cac203
		switch(errno) {
Packit cac203
		case EINTR:
Packit cac203
			goto rerecv;
Packit cac203
		case ENETDOWN:
Packit cac203
			return 0;
Packit cac203
		default:
Packit cac203
			teamd_log_err("recvfrom failed.");
Packit cac203
			return -errno;
Packit cac203
		}
Packit cac203
	}
Packit cac203
	return ret;
Packit cac203
}