1 /* $OpenBSD: cmsgsize.c,v 1.3 2020/01/22 07:52:37 deraadt Exp $ */ 2 /* 3 * Copyright (c) 2017 Alexander Markert <alexander.markert@siemens.com> 4 * Copyright (c) 2018 Alexander Bluhm <bluhm@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/socket.h> 21 22 #include <arpa/inet.h> 23 #include <netinet/in.h> 24 25 #include <err.h> 26 #include <errno.h> 27 #include <stdio.h> 28 #include <string.h> 29 #include <unistd.h> 30 31 #define CFG_PORT 5000 32 #define CFG_SO_MAX_SEND_BUFFER 1024 33 34 char payload[CFG_SO_MAX_SEND_BUFFER]; 35 36 int test_cmsgsize(int, struct in_addr *, struct in_addr *, 37 unsigned int, unsigned int); 38 39 int 40 main(int argc, char *argv[]) 41 { 42 int so, bytes; 43 struct in_addr src, dst; 44 45 if (argc != 3) 46 errx(2, "usage: %s <source_address> <destination_address>", 47 argv[0]); 48 49 if (inet_pton(AF_INET, argv[1], &src) != 1) 50 err(1, "unable to parse source address"); 51 if (inet_pton(AF_INET, argv[2], &dst) != 1) 52 err(1, "unable to parse destination address"); 53 54 /* 1: !blocking, cmsg + payload > sndbufsize => EMSGSIZE */ 55 so = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); 56 if (so < 0) 57 err(1, "1: socket"); 58 bytes = test_cmsgsize(so, &src, &dst, CFG_SO_MAX_SEND_BUFFER, 59 CFG_SO_MAX_SEND_BUFFER); 60 if (bytes >= 0) 61 errx(1, "1: %d bytes sent\n", bytes); 62 if (errno != EMSGSIZE) 63 err(-1, "1: incorrect errno"); 64 close(so); 65 66 /* 2: blocking, cmsg + payload > sndbufsize => EMSGSIZE */ 67 so = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 68 if (so < 0) 69 err(1, "2: socket"); 70 bytes = test_cmsgsize(so, &src, &dst, CFG_SO_MAX_SEND_BUFFER, 71 CFG_SO_MAX_SEND_BUFFER); 72 if (bytes >= 0) 73 errx(1, "2: %d bytes sent\n", bytes); 74 if (errno != EMSGSIZE) 75 err(-1, "2: incorrect errno"); 76 close(so); 77 78 /* 3: !blocking, cmsg + payload < sndbufsize => OK */ 79 so = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); 80 if (so < 0) 81 err(1, "3: socket 3"); 82 bytes = test_cmsgsize(so, &src, &dst, CFG_SO_MAX_SEND_BUFFER, 83 CFG_SO_MAX_SEND_BUFFER/2); 84 if (bytes < 0) 85 err(1, "3: got errno"); 86 close(so); 87 88 /* 4: blocking, cmsg + payload < sndbufsize => OK */ 89 so = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 90 if (so < 0) 91 err(1, "4: socket"); 92 bytes = test_cmsgsize(so, &src, &dst, CFG_SO_MAX_SEND_BUFFER, 93 CFG_SO_MAX_SEND_BUFFER/2); 94 if (bytes < 0) 95 err(4, "3: got errno"); 96 close(so); 97 98 return 0; 99 } 100 101 int 102 test_cmsgsize(int so, struct in_addr *src, struct in_addr *dst, 103 unsigned int sndbuf_size, unsigned int payload_size) 104 { 105 char cmsgbuf[CMSG_SPACE(sizeof(struct in_addr))]; 106 struct sockaddr_in to; 107 struct in_addr *source_address; 108 struct msghdr msg; 109 struct cmsghdr *cmsg; 110 struct iovec iov; 111 112 if (setsockopt(so, SOL_SOCKET, SO_SNDBUF, &sndbuf_size, 113 sizeof(sndbuf_size)) < 0) 114 err(1, "setsockopt send buffer"); 115 116 /* setup remote address */ 117 memset(&to, 0, sizeof(to)); 118 to.sin_family = AF_INET; 119 to.sin_addr = *dst; 120 to.sin_port = htons(CFG_PORT); 121 122 /* setup buffer to be sent */ 123 memset(&msg, 0, sizeof(msg)); 124 msg.msg_name = &to; 125 msg.msg_namelen = sizeof(to); 126 iov.iov_base = payload; 127 iov.iov_len = payload_size; 128 msg.msg_iovlen = 1; 129 msg.msg_iov = &iov; 130 131 /* setup configuration for source address */ 132 memset(cmsgbuf, 0, sizeof(cmsgbuf)); 133 msg.msg_control = cmsgbuf; 134 msg.msg_controllen = sizeof(cmsgbuf); 135 cmsg = CMSG_FIRSTHDR(&msg); 136 cmsg->cmsg_level = IPPROTO_IP; 137 cmsg->cmsg_type = IP_SENDSRCADDR; 138 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); 139 source_address = (struct in_addr *)(CMSG_DATA(cmsg)); 140 memcpy(source_address, src, sizeof(struct in_addr)); 141 142 return sendmsg(so, &msg, 0); 143 } 144