1#ifdef HAVE_CONFIG_H 2#include "config.h" 3#endif 4 5#include <sys/param.h> 6 7#include <stdlib.h> 8#include <string.h> 9#include <errno.h> 10 11#include <ctype.h> 12#include <netinet/in.h> 13#include <sys/mman.h> 14#include <sys/socket.h> 15#include <sys/types.h> 16#include <unistd.h> 17 18#include "snf.h" 19#include "pcap-int.h" 20 21#ifdef SNF_ONLY 22#define snf_create pcap_create 23#define snf_platform_finddevs pcap_platform_finddevs 24#endif 25 26static int 27snf_set_datalink(pcap_t *p, int dlt) 28{ 29 p->linktype = dlt; 30 return (0); 31} 32 33static int 34snf_pcap_stats(pcap_t *p, struct pcap_stat *ps) 35{ 36 struct snf_ring_stats stats; 37 int rc; 38 39 if ((rc = snf_ring_getstats(p->md.snf_ring, &stats))) { 40 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "snf_get_stats: %s", 41 pcap_strerror(rc)); 42 return -1; 43 } 44 ps->ps_recv = stats.ring_pkt_recv + stats.ring_pkt_overflow; 45 ps->ps_drop = stats.ring_pkt_overflow; 46 ps->ps_ifdrop = stats.nic_pkt_overflow + stats.nic_pkt_bad; 47 return 0; 48} 49 50static void 51snf_platform_cleanup(pcap_t *p) 52{ 53 if (p == NULL) 54 return; 55 56 snf_ring_close(p->md.snf_ring); 57 snf_close(p->md.snf_handle); 58 pcap_cleanup_live_common(p); 59} 60 61static int 62snf_getnonblock(pcap_t *p, char *errbuf) 63{ 64 return (p->md.snf_timeout == 0); 65} 66 67static int 68snf_setnonblock(pcap_t *p, int nonblock, char *errbuf) 69{ 70 if (nonblock) 71 p->md.snf_timeout = 0; 72 else { 73 if (p->md.timeout <= 0) 74 p->md.snf_timeout = -1; /* forever */ 75 else 76 p->md.snf_timeout = p->md.timeout; 77 } 78 return (0); 79} 80 81#define _NSEC_PER_SEC 1000000000 82 83static inline 84struct timeval 85snf_timestamp_to_timeval(const int64_t ts_nanosec) 86{ 87 struct timeval tv; 88 int32_t rem; 89 if (ts_nanosec == 0) 90 return (struct timeval) { 0, 0 }; 91 tv.tv_sec = ts_nanosec / _NSEC_PER_SEC; 92 tv.tv_usec = (ts_nanosec % _NSEC_PER_SEC) / 1000; 93 return tv; 94} 95 96static int 97snf_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) 98{ 99 struct pcap_pkthdr hdr; 100 int i, flags, err, caplen, n; 101 struct snf_recv_req req; 102 103 if (!p || cnt == 0) 104 return -1; 105 106 n = 0; 107 while (n < cnt || cnt < 0) { 108 /* 109 * Has "pcap_breakloop()" been called? 110 */ 111 if (p->break_loop) { 112 if (n == 0) { 113 p->break_loop = 0; 114 return (-2); 115 } else { 116 return (n); 117 } 118 } 119 120 err = snf_ring_recv(p->md.snf_ring, p->md.snf_timeout, &req); 121 122 if (err) { 123 if (err == EBUSY || err == EAGAIN) 124 return (0); 125 if (err == EINTR) 126 continue; 127 if (err != 0) { 128 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "snf_read: %s", 129 pcap_strerror(err)); 130 return -1; 131 } 132 } 133 134 caplen = req.length; 135 if (caplen > p->snapshot) 136 caplen = p->snapshot; 137 138 if ((p->fcode.bf_insns == NULL) || 139 bpf_filter(p->fcode.bf_insns, req.pkt_addr, req.length, caplen)) { 140 hdr.ts = snf_timestamp_to_timeval(req.timestamp); 141 hdr.caplen = caplen; 142 hdr.len = req.length; 143 callback(user, &hdr, req.pkt_addr); 144 } 145 n++; 146 } 147 return (n); 148} 149 150static int 151snf_setfilter(pcap_t *p, struct bpf_program *fp) 152{ 153 if (!p) 154 return -1; 155 if (!fp) { 156 strncpy(p->errbuf, "setfilter: No filter specified", 157 sizeof(p->errbuf)); 158 return -1; 159 } 160 161 /* Make our private copy of the filter */ 162 163 if (install_bpf_program(p, fp) < 0) 164 return -1; 165 166 p->md.use_bpf = 0; 167 168 return (0); 169} 170 171static int 172snf_inject(pcap_t *p, const void *buf _U_, size_t size _U_) 173{ 174 strlcpy(p->errbuf, "Sending packets isn't supported with snf", 175 PCAP_ERRBUF_SIZE); 176 return (-1); 177} 178 179static int 180snf_activate(pcap_t* p) 181{ 182 char *device = p->opt.source; 183 const char *nr = NULL; 184 int err; 185 int flags = 0; 186 187 if (device == NULL) { 188 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, 189 "device is NULL: %s", pcap_strerror(errno)); 190 return -1; 191 } 192 193 /* In Libpcap, we set pshared by default if NUM_RINGS is set to > 1. 194 * Since libpcap isn't thread-safe */ 195 if ((nr = getenv("SNF_NUM_RINGS")) && *nr && atoi(nr) > 1) 196 flags |= SNF_F_PSHARED; 197 else 198 nr = NULL; 199 200 err = snf_open(p->md.snf_boardnum, 201 0, /* let SNF API parse SNF_NUM_RINGS, if set */ 202 NULL, /* default RSS, or use SNF_RSS_FLAGS env */ 203 0, /* default to SNF_DATARING_SIZE from env */ 204 flags, /* may want pshared */ 205 &p->md.snf_handle); 206 if (err != 0) { 207 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, 208 "snf_open failed: %s", pcap_strerror(err)); 209 return -1; 210 } 211 212 err = snf_ring_open(p->md.snf_handle, &p->md.snf_ring); 213 if (err != 0) { 214 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, 215 "snf_ring_open failed: %s", pcap_strerror(err)); 216 return -1; 217 } 218 219 if (p->md.timeout <= 0) 220 p->md.snf_timeout = -1; 221 else 222 p->md.snf_timeout = p->md.timeout; 223 224 err = snf_start(p->md.snf_handle); 225 if (err != 0) { 226 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, 227 "snf_start failed: %s", pcap_strerror(err)); 228 return -1; 229 } 230 231 /* 232 * "select()" and "poll()" don't work on snf descriptors. 233 */ 234 p->selectable_fd = -1; 235 p->linktype = DLT_EN10MB; 236 p->read_op = snf_read; 237 p->inject_op = snf_inject; 238 p->setfilter_op = snf_setfilter; 239 p->setdirection_op = NULL; /* Not implemented.*/ 240 p->set_datalink_op = snf_set_datalink; 241 p->getnonblock_op = snf_getnonblock; 242 p->setnonblock_op = snf_setnonblock; 243 p->stats_op = snf_pcap_stats; 244 p->cleanup_op = snf_platform_cleanup; 245 p->md.stat.ps_recv = 0; 246 p->md.stat.ps_drop = 0; 247 p->md.stat.ps_ifdrop = 0; 248 return 0; 249} 250 251int 252snf_platform_finddevs(pcap_if_t **devlistp, char *errbuf) 253{ 254 /* 255 * There are no platform-specific devices since each device 256 * exists as a regular Ethernet device. 257 */ 258 return 0; 259} 260 261pcap_t * 262snf_create(const char *device, char *ebuf) 263{ 264 pcap_t *p; 265 int boardnum = -1; 266 struct snf_ifaddrs *ifaddrs, *ifa; 267 size_t devlen; 268 269 if (snf_init(SNF_VERSION_API)) 270 return NULL; 271 272 /* 273 * Match a given interface name to our list of interface names, from 274 * which we can obtain the intended board number 275 */ 276 if (snf_getifaddrs(&ifaddrs) || ifaddrs == NULL) 277 return NULL; 278 devlen = strlen(device) + 1; 279 ifa = ifaddrs; 280 while (ifa) { 281 if (!strncmp(device, ifa->snf_ifa_name, devlen)) { 282 boardnum = ifa->snf_ifa_boardnum; 283 break; 284 } 285 ifa = ifa->snf_ifa_next; 286 } 287 snf_freeifaddrs(ifaddrs); 288 289 if (ifa == NULL) { 290 /* 291 * If we can't find the device by name, support the name "snfX" 292 * and "snf10gX" where X is the board number. 293 */ 294 if (sscanf(device, "snf10g%d", &boardnum) != 1 && 295 sscanf(device, "snf%d", &boardnum) != 1) 296 return NULL; 297 } 298 299 p = pcap_create_common(device, ebuf); 300 if (p == NULL) 301 return NULL; 302 303 p->activate_op = snf_activate; 304 p->md.snf_boardnum = boardnum; 305 return p; 306} 307