|
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 |
}
|