#include
/**
* @brief 设置与某个套接字关联的选项
*
* @param fd 套接字
* @param level:选项所在的协议层,支持SOLSOCKET、IPPROTOTCP、IPPROTOIP和IPPROTOIPV6
* 一般选择SOLSOCKET
* @param optname:需设置的选项,而有部分选项需在listen/connect调用前设置才有效
* @param optval:指针,指向存放选项值的缓冲区
* @param optlen:长度
* @return int: 0 返回设置成功,-1 设置失败,errno 出错原因
*/
extern int setsockopt (int fd, int level, int optname,
const void *optval, socklen_t optlen);
extern int getsockopt (int fd, int level, int optname,
void *restrict optval,
socklen_t *restrict optlen);
linux 5.10 内核系统调用声明
// net/socket.c
SYSCALL_DEFINE5(setsockopt, int, fd, int, level, int, optname,
char __user *, optval, int, optlen)
{
return __sys_setsockopt(fd, level, optname, optval, optlen);
}
// net/socket.c line:2096
int __sys_setsockopt(int fd, int level, int optname, char __user *user_optval,
int optlen)
{
...
if (level == SOL_SOCKET && !sock_use_custom_sol_socket(sock))
err = sock_setsockopt(sock, level, optname, optval, optlen);
else if (unlikely(!sock->ops->setsockopt))
err = -EOPNOTSUPP;
else
err = sock->ops->setsockopt(sock, level, optname, optval,
optlen);
}
optname
选项设置, 可以参考 man 7 socket
// net/core/sock.c line:823
int sock_setsockopt(struct socket *sock, int level, int optname,
sockptr_t optval, unsigned int optlen)
{
...
if (optname == SO_BINDTODEVICE)
return sock_setbindtodevice(sk, optval, optlen);
...
switch (optname) {
case SO_DEBUG:
if (val && !capable(CAP_NET_ADMIN))
ret = -EACCES;
else
sock_valbool_flag(sk, SOCK_DBG, valbool);
break;
case SO_REUSEADDR: // 允许重用本地地址
sk->sk_reuse = (valbool ? SK_CAN_REUSE : SK_NO_REUSE);
break;
case SO_REUSEPORT: // 允许重用本地端口
sk->sk_reuseport = valbool;
break;
case SO_TYPE:
case SO_PROTOCOL:
case SO_DOMAIN:
case SO_ERROR:
ret = -ENOPROTOOPT;
break;
case SO_DONTROUTE: // 不查找路由
sock_valbool_flag(sk, SOCK_LOCALROUTE, valbool);
sk_dst_reset(sk);
break;
case SO_BROADCAST: // 允许发送广播数据
sock_valbool_flag(sk, SOCK_BROADCAST, valbool);
break;
...
case SO_KEEPALIVE: // 保持连接
if (sk->sk_prot->keepalive)
sk->sk_prot->keepalive(sk, valbool);
sock_valbool_flag(sk, SOCK_KEEPOPEN, valbool);
break;
case SO_OOBINLINE: // 带外数据放入正常数据流
sock_valbool_flag(sk, SOCK_URGINLINE, valbool);
break;
case SO_NO_CHECK:
sk->sk_no_check_tx = valbool;
break;
case SO_PRIORITY:
if ((val >= 0 && val <= 6) ||
ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
sk->sk_priority = val;
else
ret = -EPERM;
break;
....
case SO_RCVTIMEO_OLD:
case SO_RCVTIMEO_NEW:
ret = sock_set_timeout(&sk->sk_rcvtimeo, optval,
optlen, optname == SO_RCVTIMEO_OLD);
break;
case SO_SNDTIMEO_OLD:
case SO_SNDTIMEO_NEW:
ret = sock_set_timeout(&sk->sk_sndtimeo, optval,
optlen, optname == SO_SNDTIMEO_OLD);
break;
case SO_ATTACH_FILTER: {
struct sock_fprog fprog;
ret = copy_bpf_fprog_from_user(&fprog, optval, optlen);
if (!ret)
ret = sk_attach_filter(&fprog, sk);
break;
}
...
case SO_INCOMING_CPU:
WRITE_ONCE(sk->sk_incoming_cpu, val);
break;
case SO_CNX_ADVICE:
if (val == 1)
dst_negative_advice(sk);
break;
case SO_ZEROCOPY:
...
break;
...
default:
ret = -ENOPROTOOPT;
break;
}
release_sock(sk);
return ret;
}
参考setsockopt 的 SO_BINDTODEVICE 套接口选项
sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct ifreq interface;
// 网卡名称
strncpy(interface.ifr_name, "wlp3s0", IFNAMSIZ);
if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, (char *)&interface, sizeof(interface)) < 0) {
perror("wlp3s0-client:SO_BINDTODEVICE failed");
return -1;
}