SOCKET选项
用法:
int getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen);
int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);
实例
IP多播模型(VRRP):
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <linux/tcp.h>
#include <linux/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <asm-generic/ioctls.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
/* 192.168.220.128 */
#define LOCAL_ADDR 0xC0A8DC80
/* 192.168.220.129 */
#define PEER_ADDR 0xC0A8DC81
#define IPPROTO_VRRP 0x70
/* 224.0.0.18 */
#define MCAST_ADDR 0xe0000012
#define MODE_LOCAL 0
#define MODE_PEER 1
#define BUFFER_SIZE 128
extern int errno;
int mode = 0; /* local or peer */
int sockFd = 0;
long int lastTime = 0;
int socketInit()
{
int on = 0;
int ret = 0;
struct ip_mreq mreq = {0};
if((sockFd = socket(AF_INET, SOCK_RAW, IPPROTO_VRRP)) < 0)
{
perror("socket");
exit(1);
}
/* 设置非阻塞模式 */
fcntl(sockFd, F_SETFL, O_NONBLOCK);
/* 设置ttl */
on = 255;
if ((ret = setsockopt (sockFd, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&on, sizeof(int))) < 0)
{
perror("setsockopt IP_MULTICAST_TTL");
exit(1);
}
/* 禁止组播包环回 */
on = 0;
if ((ret = setsockopt (sockFd, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&on, sizeof(int))) < 0)
{
perror("setsockopt IP_MULTICAST_LOOP");
exit(1);
}
/* 加入多播组,用于收包 */
mreq.imr_multiaddr.s_addr = htonl(MCAST_ADDR);
#if 0
mreq.imr_interface.s_addr = mode == MODE_LOCAL ? htonl(LOCAL_ADDR) : htonl(PEER_ADDR);
#else
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
#endif
if ((ret = setsockopt(sockFd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq))) < 0)
{
perror("setsockopt IP_ADD_MEMBERSHIP");
exit(1);
}
/* 携带接口信息 */
on = 1;
if ((ret = setsockopt(sockFd, IPPROTO_IP, IP_PKTINFO, (char *)&on, sizeof(int))) < 0)
{
perror("setsockopt IP_PKTINFO");
exit(1);
}
/* 设置自定义IP头,否则系统会自己封装IP头 */
on = 1;
if ((ret = setsockopt(sockFd, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(int))) < 0)
{
perror("setsockopt IP_HDRINCL");
exit(1);
}
return 0;
}
int socketRcv(int fd)
{
char recvBuf[BUFFER_SIZE] = {0};
int recvLen = 0;
time_t rawtime;
time(&rawtime);
printf("%s",ctime(&rawtime));
/* 这里简单的用recv收ip包,还可以使用recvmsg将控制信息(接口索引和地址)带进来,前面设置了IP_PKTINFO */
/*
struct in_pktinfo {
int ipi_ifindex;
struct in_addr ipi_spec_dst;
struct in_addr ipi_addr;
};*/
recvLen = recv(fd, recvBuf, BUFFER_SIZE, 0);
if (recvLen <= 0)
{
perror("recv");
exit(1);
}
recvBuf[recvLen] = 0; /* 结束符 */
printf("recv %d bytes from peer:%s\n",recvLen,recvBuf+20); /* RAW socket收上来的包带20字节的IP头 */
return 0;
}
int socketSend(int fd)
{
struct sockaddr_in dst_addr = {0};
struct msghdr msg = {0};
struct iovec iov[2] = {0};
struct ip_mreq mreq = {0};
struct iphdr ip_header;
struct in_addr addr = {0};
char vrrp_pkt[32] = "VRRP PKT TEST ONLY!!!!!!!!!!!!!";
char mac[6] = {0x00,0x00,0x5e,0x00,0x01,0x01};
int ret = 0;
if (fd <= 0)
{
return 0;
}
/* 设置IP头部 */
memset(&ip_header, 0, sizeof(struct iphdr));
ip_header.version = 4;
ip_header.ihl = (sizeof(struct iphdr) >> 2) & 0xf;
ip_header.tos = IPTOS_PREC_INTERNETCONTROL;
ip_header.id = 0;
ip_header.frag_off = 0;
ip_header.ttl = 255;
ip_header.protocol = IPPROTO_VRRP;
ip_header.check = 0;
ip_header.tot_len = htons(sizeof(struct iphdr) + sizeof(vrrp_pkt));
ip_header.saddr = mode == MODE_LOCAL ? htonl(LOCAL_ADDR) : htonl(PEER_ADDR);
ip_header.daddr = htonl(MCAST_ADDR);
/* 设置目的地址 */
dst_addr.sin_family = AF_INET;
dst_addr.sin_addr.s_addr = htonl(MCAST_ADDR);
/* 设置msg */
msg.msg_name = &dst_addr;
msg.msg_namelen = sizeof (dst_addr);
msg.msg_iov = iov;
msg.msg_iovlen = 2;
/* 设置缓冲区 */
iov[0].iov_base = (char *)&ip_header;
iov[0].iov_len = sizeof(struct iphdr);
iov[1].iov_base = (char *)vrrp_pkt;
iov[1].iov_len = sizeof(vrrp_pkt);
/* 设置多播出接口 */
addr.s_addr = mode == MODE_LOCAL ? htonl(LOCAL_ADDR) : htonl(PEER_ADDR);
if ((ret = setsockopt(sockFd, IPPROTO_IP, IP_MULTICAST_IF, (char *)&addr, sizeof(addr))) < 0)
{
perror("setsockopt IP_MULTICAST_IF");
exit(1);
}
#if 0
/* 设置发包源MAC */
if ((ret = setsockopt(sockFd, SOL_SOCKET, SO_SNDSRCMAC, (char *)&mac, sizeof(mac))) < 0)
{
perror("setsockopt SO_SNDSRCMAC");
exit(1);
}
#endif
if ((ret = sendmsg(sockFd, &msg, MSG_DONTWAIT)) < 0)
{
perror("sendmsg");
exit(1);
}
return 0;
}
int send_timer()
{
long int currTime = time(NULL);
/* 每隔1s发送一次 */
if (currTime - lastTime >= 1)
{
socketSend(sockFd);
/* 更新上次发送时间 */
lastTime = currTime;
}
return 0;
}
int main(int argc,char **argv)
{
fd_set rbits;
struct timeval t;
int n = 0; /* 读写可用的socket个数 */
if (argc != 2)
{
printf("usage: ./ip_mc_test local|peer\n");
return 0;
}
/* 启动local或者peer,local和peer主要用于在两台机器上(ip地址不同)测试 */
if (memcmp(argv[1], "local", strlen("local")) == 0)
{
mode = MODE_LOCAL;
}
else if (memcmp(argv[1], "peer", strlen("peer")) == 0)
{
mode = MODE_PEER;
}
else
{
printf("usage: ./ip_mc_test local|peer\n");
return 0;
}
socketInit();
/* 主进程循环 */
while(1)
{
FD_ZERO(&rbits);
t.tv_sec = 1;
t.tv_usec = 0;
if (sockFd > 0)
FD_SET(sockFd, &rbits);
/* 检查套接字可读可写性,n为返回的可读和可写的套接字个数 */
n = select(sockFd + 1, &rbits, NULL, NULL, &t);
if (n > 0)
{
/* 可读,即有数据或者请求到达(serverFd只负责accept连接请求) */
if (FD_ISSET(sockFd, &rbits))
{
socketRcv(sockFd);
}
}
/* 发包定时器 */
send_timer();
}
return 0;
}