1 /*
2  * Copyright (c) 2012 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Sepherosa Ziehau <sepherosa@gmail.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include <sys/types.h>
36 #include <sys/ioctl.h>
37 #include <sys/stat.h>
38 #include <sys/socket.h>
39 
40 #include <arpa/inet.h>
41 #include <netinet/in.h>
42 #include <net/if.h>
43 #include <net/if_dl.h>
44 #include <net/ethernet.h>
45 
46 #include <err.h>
47 #include <fcntl.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52 
53 #include "pktgen/pktgen.h"
54 
55 #define PKTGEN_DEVPATH	"/dev/pktg0"
56 
57 #define DEFAULT_PORT	7000
58 
59 #define INDST_MASK	0x0001
60 #define INSRC_MASK	0x0002
61 #define EADDR_MASK	0x0004
62 #define IFACE_MASK	0x0008
63 
64 #define MASK_NEEDED	(INDST_MASK | INSRC_MASK | EADDR_MASK | IFACE_MASK)
65 
66 static void
67 usage(void)
68 {
69 	fprintf(stderr, "pktgenctl "
70 	    "-d dst_inaddr[:dst_port] [-d dst_inaddr[:dst_port] ...] "
71 	    "-s src_inaddr[:src_port] "
72 	    "-e (gw_eaddr|dst_eaddr) -i iface "
73 	    "[-m data_len] [-l duration] [-D dev] [-q pktenq] [-M] [-S]\n");
74 	exit(1);
75 }
76 
77 static int
78 get_port(char *str)
79 {
80 	char *p;
81 
82 	p = strchr(str, ':');
83 	if (p == NULL) {
84 		return DEFAULT_PORT;
85 	} else {
86 		*p = '\0';
87 		++p;
88 		return atoi(p);
89 	}
90 }
91 
92 int
93 main(int argc, char *argv[])
94 {
95 	struct pktgen_conf conf;
96 	struct sockaddr *sa;
97 	struct sockaddr_in *src_sin, *dst_sin;
98 	struct sockaddr_dl sdl;
99 	char eaddr_str[32];
100 	uint32_t arg_mask = 0;
101 	int fd, c, n, ndst_alloc;
102 	const char *dev;
103 	u_long start = PKTGENSTART;
104 
105 	dev = PKTGEN_DEVPATH;
106 
107 	memset(&conf, 0, sizeof(conf));
108 
109 	conf.pc_duration = 10;
110 	conf.pc_datalen = 18;
111 
112 	sa = &conf.pc_dst_lladdr;
113 	sa->sa_family = AF_LINK;
114 	sa->sa_len = ETHER_ADDR_LEN;
115 
116 	src_sin = &conf.pc_src;
117 	src_sin->sin_family = AF_INET;
118 
119 	ndst_alloc = 1;
120 	conf.pc_dst = calloc(ndst_alloc, sizeof(struct sockaddr_in));
121 	if (conf.pc_dst == NULL)
122 		err(1, "calloc(%d dst)", ndst_alloc);
123 
124 	conf.pc_ndst = 0;
125 	while ((c = getopt(argc, argv, "d:s:e:i:m:l:D:q:MS")) != -1) {
126 		switch (c) {
127 		case 'd':
128 			if (conf.pc_ndst >= ndst_alloc) {
129 				void *old = conf.pc_dst;
130 				int old_ndst_alloc = ndst_alloc;
131 
132 				ndst_alloc *= 2;
133 				conf.pc_dst = calloc(ndst_alloc,
134 				    sizeof(struct sockaddr_in));
135 				if (conf.pc_dst == NULL)
136 					err(1, "calloc(%d dst)", ndst_alloc);
137 				memcpy(conf.pc_dst, old,
138 				old_ndst_alloc * sizeof(struct sockaddr_in));
139 				free(old);
140 			}
141 			dst_sin = &conf.pc_dst[conf.pc_ndst++];
142 
143 			dst_sin->sin_family = AF_INET;
144 			dst_sin->sin_port = get_port(optarg);
145 			dst_sin->sin_port = htons(dst_sin->sin_port);
146 			n = inet_pton(AF_INET, optarg, &dst_sin->sin_addr);
147 			if (n == 0)
148 				errx(1, "-d: invalid inet address: %s", optarg);
149 			else if (n < 0)
150 				err(1, "-d: %s", optarg);
151 			arg_mask |= INDST_MASK;
152 			break;
153 
154 		case 's':
155 			src_sin->sin_port = get_port(optarg);
156 			src_sin->sin_port = htons(src_sin->sin_port);
157 			n = inet_pton(AF_INET, optarg, &src_sin->sin_addr);
158 			if (n == 0)
159 				errx(1, "-s: invalid inet address: %s", optarg);
160 			else if (n < 0)
161 				err(1, "-s: %s", optarg);
162 			arg_mask |= INSRC_MASK;
163 			break;
164 
165 		case 'e':
166 			strcpy(eaddr_str, "if0.");
167 			strlcpy(&eaddr_str[strlen("if0.")], optarg,
168 			    sizeof(eaddr_str) - strlen("if0."));
169 
170 			memset(&sdl, 0, sizeof(sdl));
171 			sdl.sdl_len = sizeof(sdl);
172 			link_addr(eaddr_str, &sdl);
173 			bcopy(LLADDR(&sdl), sa->sa_data, ETHER_ADDR_LEN);
174 			arg_mask |= EADDR_MASK;
175 			break;
176 
177 		case 'i':
178 			strlcpy(conf.pc_ifname, optarg, sizeof(conf.pc_ifname));
179 			arg_mask |= IFACE_MASK;
180 			break;
181 
182 		case 'm':
183 			conf.pc_datalen = atoi(optarg);
184 			break;
185 
186 		case 'l':
187 			conf.pc_duration = atoi(optarg);
188 			break;
189 
190 		case 'D':
191 			dev = optarg;
192 			break;
193 
194 		case 'q':
195 			conf.pc_pktenq = atoi(optarg);
196 			break;
197 
198 		case 'M':
199 			start = PKTGENMQSTART;
200 			break;
201 
202 		case 'S':
203 			conf.pc_flags |= PKTGEN_FLAG_SWITCH_SRCDST;
204 			break;
205 
206 		default:
207 			usage();
208 		}
209 	}
210 
211 	if ((arg_mask & MASK_NEEDED) != MASK_NEEDED)
212 		usage();
213 
214 	fd = open(dev, O_RDONLY);
215 	if (fd < 0)
216 		err(1, "open(%s)", dev);
217 
218 	if (ioctl(fd, PKTGENSCONF, &conf) < 0)
219 		err(1, "ioctl(PKTGENSCONF)");
220 
221 	if (ioctl(fd, start) < 0) {
222 		err(1, "ioctl(%s)",
223 		    start == PKTGENSTART ? "PKTGENSTART" : "PKTGENMQSTART");
224 	}
225 
226 	close(fd);
227 	exit(0);
228 }
229