diff --git a/src/source-af-packet.c b/src/source-af-packet.c index 296ebc2e7e..e3f55b6d24 100644 --- a/src/source-af-packet.c +++ b/src/source-af-packet.c @@ -120,6 +120,7 @@ TmEcode NoAFPSupportExit(ThreadVars *tv, void *initdata, void **data) #define AFP_STATE_UP 1 #define AFP_RECONNECT_TIMEOUT 500000 +#define AFP_DOWN_COUNTER_INTERVAL 40 #define POLL_TIMEOUT 100 @@ -165,6 +166,7 @@ typedef struct AFPThreadVars_ char iface[AFP_IFACE_NAME_LENGTH]; LiveDevice *livedev; + int down_count; /* Filter */ char *bpf_filter; @@ -209,6 +211,9 @@ TmEcode DecodeAFP(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *); TmEcode AFPSetBPFFilter(AFPThreadVars *ptv); static int AFPGetIfnumByDev(int fd, const char *ifname, int verbose); +static int AFPGetDevFlags(int fd, const char *ifname); +static int AFPDerefSocket(AFPPeer* peer); +static int AFPRefSocket(AFPPeer* peer); /** * \brief Registration Function for RecieveAFP. @@ -330,6 +335,7 @@ TmEcode AFPPeersListAdd(AFPThreadVars *ptv) } memset(peer, 0, sizeof(AFPPeer)); SC_ATOMIC_INIT(peer->socket); + SC_ATOMIC_INIT(peer->sock_usage); SC_ATOMIC_INIT(peer->if_idx); SC_ATOMIC_INIT(peer->state); peer->flags = ptv->flags; @@ -338,33 +344,37 @@ TmEcode AFPPeersListAdd(AFPThreadVars *ptv) SCMutexInit(&peer->sock_protect, NULL); } + (void)SC_ATOMIC_SET(peer->sock_usage, 0); (void)SC_ATOMIC_SET(peer->state, AFP_STATE_DOWN); strlcpy(peer->iface, ptv->iface, AFP_IFACE_NAME_LENGTH); ptv->mpeer = peer; /* add element to iface list */ TAILQ_INSERT_TAIL(&peerslist.peers, peer, next); - peerslist.cnt++; - /* Iter to find a peer */ - TAILQ_FOREACH(pitem, &peerslist.peers, next) { - if (pitem->peer) - continue; - if (strcmp(pitem->iface, ptv->out_iface)) - continue; - peer->peer = pitem; - pitem->peer = peer; - mtu = GetIfaceMTU(ptv->iface); - out_mtu = GetIfaceMTU(ptv->out_iface); - if (mtu != out_mtu) { - SCLogError(SC_ERR_AFP_CREATE, - "MTU on %s (%d) and %s (%d) are not equal, " - "transmission of packets bigger than %d will fail.", - ptv->iface, mtu, - ptv->out_iface, out_mtu, - (out_mtu > mtu) ? mtu : out_mtu); + if (ptv->copy_mode != AFP_COPY_MODE_NONE) { + peerslist.cnt++; + + /* Iter to find a peer */ + TAILQ_FOREACH(pitem, &peerslist.peers, next) { + if (pitem->peer) + continue; + if (strcmp(pitem->iface, ptv->out_iface)) + continue; + peer->peer = pitem; + pitem->peer = peer; + mtu = GetIfaceMTU(ptv->iface); + out_mtu = GetIfaceMTU(ptv->out_iface); + if (mtu != out_mtu) { + SCLogError(SC_ERR_AFP_CREATE, + "MTU on %s (%d) and %s (%d) are not equal, " + "transmission of packets bigger than %d will fail.", + ptv->iface, mtu, + ptv->out_iface, out_mtu, + (out_mtu > mtu) ? mtu : out_mtu); + } + peerslist.peered += 2; + break; } - peerslist.peered += 2; - break; } AFPPeerUpdate(ptv); @@ -600,6 +610,10 @@ TmEcode AFPReleaseDataFromRing(ThreadVars *t, Packet *p) if ((p->afp_v.copy_mode != AFP_COPY_MODE_NONE) && !PKT_IS_PSEUDOPKT(p)) { ret = AFPWritePacket(p); } + + if (AFPDerefSocket(p->afp_v.mpeer) == 0) + return ret; + if (p->afp_v.relptr) { union thdr h; h.raw = p->afp_v.relptr; @@ -652,6 +666,8 @@ int AFPReadFromRing(AFPThreadVars *ptv) p->afp_v.relptr = h.raw; p->ReleaseData = AFPReleaseDataFromRing; + p->afp_v.mpeer = ptv->mpeer; + AFPRefSocket(ptv->mpeer); p->afp_v.copy_mode = ptv->copy_mode; if (p->afp_v.copy_mode != AFP_COPY_MODE_NONE) { @@ -749,16 +765,90 @@ next_frame: SCReturnInt(AFP_READ_OK); } +/** + * \brief Reference socket + * + * \retval O in case of failure, 1 in case of success + */ +static int AFPRefSocket(AFPPeer* peer) +{ + if (unlikely(peer == NULL)) + return 0; + + (void)SC_ATOMIC_ADD(peer->sock_usage, 1); + return 1; +} + + +/** + * \brief Dereference socket + * + * \retval 1 if socket is still alive, 0 if not + */ +static int AFPDerefSocket(AFPPeer* peer) +{ + if (SC_ATOMIC_SUB(peer->sock_usage, 1) == 0) { + if (SC_ATOMIC_GET(peer->state) == AFP_STATE_DOWN) { + SCLogInfo("Cleaning socket connected to '%s'", peer->iface); + close(SC_ATOMIC_GET(peer->socket)); + return 0; + } + } + return 1; +} + +void AFPSwitchState(AFPThreadVars *ptv, int state) +{ + ptv->afp_state = state; + ptv->down_count = 0; + AFPPeerUpdate(ptv); + + /* Do cleaning if switching to down state */ + if (state == AFP_STATE_DOWN) { + if (ptv->frame_buf) { + /* only used in reading phase, we can free it */ + SCFree(ptv->frame_buf); + ptv->frame_buf = NULL; + } + if (ptv->socket != -1) { + /* we need to wait for all packets to return data */ + if (SC_ATOMIC_SUB(ptv->mpeer->sock_usage, 1) == 0) { + SCLogInfo("Cleaning socket connected to '%s'", ptv->iface); + close(ptv->socket); + ptv->socket = -1; + } + } + } + if (state == AFP_STATE_UP) { + (void)SC_ATOMIC_SET(ptv->mpeer->sock_usage, 1); + } +} +/** + * \brief Try to reopen socket + * + * \retval 0 in case of success, negative if error occurs or a condition + * is not met. + */ static int AFPTryReopen(AFPThreadVars *ptv) { int afp_activate_r; - ptv->afp_state = AFP_STATE_DOWN; + ptv->down_count++; + + + /* Don't reconnect till we have packet that did not release data */ + if (SC_ATOMIC_GET(ptv->mpeer->sock_usage) != 0) { + return -1; + } afp_activate_r = AFPCreateSocket(ptv, ptv->iface, 0); if (afp_activate_r != 0) { + if (ptv->down_count % AFP_DOWN_COUNTER_INTERVAL == 0) { + SCLogWarning(SC_ERR_AFP_CREATE, "Can not open iface '%s'", + ptv->iface); + } return afp_activate_r; } @@ -789,8 +879,6 @@ TmEcode ReceiveAFPLoop(ThreadVars *tv, void *data, void *slot) if (unlikely(ptv->afp_state == AFP_STATE_DOWN)) { int dbreak = 0; - AFPPeerUpdate(ptv); - do { usleep(AFP_RECONNECT_TIMEOUT); if (suricata_ctl_flags != 0) { @@ -822,8 +910,7 @@ TmEcode ReceiveAFPLoop(ThreadVars *tv, void *data, void *slot) if (r > 0 && (fds.revents & (POLLHUP|POLLRDHUP|POLLERR|POLLNVAL))) { if (fds.revents & (POLLHUP | POLLRDHUP)) { - close(ptv->socket); - ptv->afp_state = AFP_STATE_DOWN; + AFPSwitchState(ptv, AFP_STATE_DOWN); continue; } else if (fds.revents & POLLERR) { char c; @@ -832,13 +919,11 @@ TmEcode ReceiveAFPLoop(ThreadVars *tv, void *data, void *slot) continue; /* what, no error? */ SCLogError(SC_ERR_AFP_READ, "Error reading data from socket: (%d" PRIu32 ") %s", errno, strerror(errno)); - close(ptv->socket); - ptv->afp_state = AFP_STATE_DOWN; + AFPSwitchState(ptv, AFP_STATE_DOWN); continue; } else if (fds.revents & POLLNVAL) { SCLogError(SC_ERR_AFP_READ, "Invalid polling request"); - close(ptv->socket); - ptv->afp_state = AFP_STATE_DOWN; + AFPSwitchState(ptv, AFP_STATE_DOWN); continue; } } else if (r > 0) { @@ -853,11 +938,10 @@ TmEcode ReceiveAFPLoop(ThreadVars *tv, void *data, void *slot) /* AFPRead in error: best to reset the socket */ SCLogError(SC_ERR_AFP_READ, "AFPRead error reading data from socket: (%d" PRIu32 ") %s", errno, strerror(errno)); - close(ptv->socket); - ptv->afp_state = AFP_STATE_DOWN; + AFPSwitchState(ptv, AFP_STATE_DOWN); continue; case AFP_FAILURE: - ptv->afp_state = AFP_STATE_DOWN; + AFPSwitchState(ptv, AFP_STATE_DOWN); SCReturnInt(TM_ECODE_FAILED); break; case AFP_READ_OK: @@ -870,8 +954,7 @@ TmEcode ReceiveAFPLoop(ThreadVars *tv, void *data, void *slot) } else if ((r < 0) && (errno != EINTR)) { SCLogError(SC_ERR_AFP_READ, "Error reading data from socket: (%d" PRIu32 ") %s", errno, strerror(errno)); - close(ptv->socket); - ptv->afp_state = AFP_STATE_DOWN; + AFPSwitchState(ptv, AFP_STATE_DOWN); continue; } SCPerfSyncCountersIfSignalled(tv, 0); @@ -880,6 +963,23 @@ TmEcode ReceiveAFPLoop(ThreadVars *tv, void *data, void *slot) SCReturnInt(TM_ECODE_OK); } +static int AFPGetDevFlags(int fd, const char *ifname) +{ + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + + if (ioctl(fd, SIOCGIFFLAGS, &ifr) == -1) { + SCLogError(SC_ERR_AFP_CREATE, "Unable to find type for iface \"%s\": %s", + ifname, strerror(errno)); + return -1; + } + + return ifr.ifr_flags; +} + + static int AFPGetIfnumByDev(int fd, const char *ifname, int verbose) { struct ifreq ifr; @@ -978,9 +1078,8 @@ static int AFPCreateSocket(AFPThreadVars *ptv, char *devname, int verbose) ptv->socket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (ptv->socket == -1) { SCLogError(SC_ERR_AFP_CREATE, "Couldn't create a AF_PACKET socket, error %s", strerror(errno)); - return -1; + goto error; } - SCLogInfo("Using interface '%s' via socket %d", (char *)devname, ptv->socket); if_idx = AFPGetIfnumByDev(ptv->socket, devname, verbose); /* bind socket */ memset(&bind_address, 0, sizeof(bind_address)); @@ -990,7 +1089,83 @@ static int AFPCreateSocket(AFPThreadVars *ptv, char *devname, int verbose) if (bind_address.sll_ifindex == -1) { if (verbose) SCLogError(SC_ERR_AFP_CREATE, "Couldn't find iface %s", devname); - return -1; + goto socket_err; + } + + + + if (ptv->promisc != 0) { + /* Force promiscuous mode */ + memset(&sock_params, 0, sizeof(sock_params)); + sock_params.mr_type = PACKET_MR_PROMISC; + sock_params.mr_ifindex = bind_address.sll_ifindex; + r = setsockopt(ptv->socket, SOL_PACKET, PACKET_ADD_MEMBERSHIP,(void *)&sock_params, sizeof(sock_params)); + if (r < 0) { + SCLogError(SC_ERR_AFP_CREATE, + "Couldn't switch iface %s to promiscuous, error %s", + devname, strerror(errno)); + goto frame_err; + } + } + + if (ptv->checksum_mode == CHECKSUM_VALIDATION_KERNEL) { + int val = 1; + if (setsockopt(ptv->socket, SOL_PACKET, PACKET_AUXDATA, &val, + sizeof(val)) == -1 && errno != ENOPROTOOPT) { + SCLogWarning(SC_ERR_NO_AF_PACKET, + "'kernel' checksum mode not supported, failling back to full mode."); + ptv->checksum_mode = CHECKSUM_VALIDATION_ENABLE; + } + } + + /* set socket recv buffer size */ + if (ptv->buffer_size != 0) { + /* + * Set the socket buffer size to the specified value. + */ + SCLogInfo("Setting AF_PACKET socket buffer to %d", ptv->buffer_size); + if (setsockopt(ptv->socket, SOL_SOCKET, SO_RCVBUF, + &ptv->buffer_size, + sizeof(ptv->buffer_size)) == -1) { + SCLogError(SC_ERR_AFP_CREATE, + "Couldn't set buffer size to %d on iface %s, error %s", + ptv->buffer_size, devname, strerror(errno)); + goto frame_err; + } + } + + r = bind(ptv->socket, (struct sockaddr *)&bind_address, sizeof(bind_address)); + if (r < 0) { + if (verbose) { + if (errno == ENETDOWN) { + SCLogError(SC_ERR_AFP_CREATE, + "Couldn't bind AF_PACKET socket, iface %s is down", + devname); + } else { + SCLogError(SC_ERR_AFP_CREATE, + "Couldn't bind AF_PACKET socket to iface %s, error %s", + devname, strerror(errno)); + } + } + goto frame_err; + } + + int if_flags = AFPGetDevFlags(ptv->socket, ptv->iface); + if (if_flags == -1) { + if (verbose) { + SCLogError(SC_ERR_AFP_READ, + "Can not acces to interface '%s'", + ptv->iface); + } + goto frame_err; + } + if ((if_flags & IFF_UP) == 0) { + if (verbose) { + SCLogError(SC_ERR_AFP_READ, + "Interface '%s' is down", + ptv->iface); + } + goto frame_err; } if (ptv->flags & AFP_RING_MODE) { @@ -1002,7 +1177,7 @@ static int AFPCreateSocket(AFPThreadVars *ptv, char *devname, int verbose) "Too old kernel giving up (need 2.6.27 at least)"); } SCLogError(SC_ERR_AFP_CREATE, "Error when retrieving packet header len"); - return -1; + goto socket_err; } ptv->tp_hdrlen = val; @@ -1012,7 +1187,7 @@ static int AFPCreateSocket(AFPThreadVars *ptv, char *devname, int verbose) SCLogError(SC_ERR_AFP_CREATE, "Can't activate TPACKET_V2 on packet socket: %s", strerror(errno)); - return -1; + goto socket_err; } /* Allocate RX ring */ @@ -1033,7 +1208,7 @@ static int AFPCreateSocket(AFPThreadVars *ptv, char *devname, int verbose) devname, errno, strerror(errno)); - return -1; + goto socket_err; } else { break; } @@ -1043,7 +1218,7 @@ static int AFPCreateSocket(AFPThreadVars *ptv, char *devname, int verbose) SCLogError(SC_ERR_MEM_ALLOC, "Unable to allocate RX Ring for iface %s (order 0 failed)", devname); - return -1; + goto socket_err; } /* Allocate the Ring */ @@ -1052,13 +1227,13 @@ static int AFPCreateSocket(AFPThreadVars *ptv, char *devname, int verbose) MAP_SHARED, ptv->socket, 0); if (ptv->ring_buf == MAP_FAILED) { SCLogError(SC_ERR_MEM_ALLOC, "Unable to mmap"); - return -1; + goto socket_err; } /* allocate a ring for each frame header pointer*/ ptv->frame_buf = SCMalloc(ptv->req.tp_frame_nr * sizeof (union thdr *)); if (ptv->frame_buf == NULL) { SCLogError(SC_ERR_MEM_ALLOC, "Unable to allocate frame buf"); - return -1; + goto mmap_err; } memset(ptv->frame_buf, 0, ptv->req.tp_frame_nr * sizeof (union thdr *)); /* fill the header ring with proper frame ptr*/ @@ -1074,64 +1249,7 @@ static int AFPCreateSocket(AFPThreadVars *ptv, char *devname, int verbose) ptv->frame_offset = 0; } - if (ptv->promisc != 0) { - /* Force promiscuous mode */ - memset(&sock_params, 0, sizeof(sock_params)); - sock_params.mr_type = PACKET_MR_PROMISC; - sock_params.mr_ifindex = bind_address.sll_ifindex; - r = setsockopt(ptv->socket, SOL_PACKET, PACKET_ADD_MEMBERSHIP,(void *)&sock_params, sizeof(sock_params)); - if (r < 0) { - SCLogError(SC_ERR_AFP_CREATE, - "Couldn't switch iface %s to promiscuous, error %s", - devname, strerror(errno)); - close(ptv->socket); - return -1; - } - } - - if (ptv->checksum_mode == CHECKSUM_VALIDATION_KERNEL) { - int val = 1; - if (setsockopt(ptv->socket, SOL_PACKET, PACKET_AUXDATA, &val, - sizeof(val)) == -1 && errno != ENOPROTOOPT) { - SCLogWarning(SC_ERR_NO_AF_PACKET, - "'kernel' checksum mode not supported, failling back to full mode."); - ptv->checksum_mode = CHECKSUM_VALIDATION_ENABLE; - } - } - - /* set socket recv buffer size */ - if (ptv->buffer_size != 0) { - /* - * Set the socket buffer size to the specified value. - */ - SCLogInfo("Setting AF_PACKET socket buffer to %d", ptv->buffer_size); - if (setsockopt(ptv->socket, SOL_SOCKET, SO_RCVBUF, - &ptv->buffer_size, - sizeof(ptv->buffer_size)) == -1) { - SCLogError(SC_ERR_AFP_CREATE, - "Couldn't set buffer size to %d on iface %s, error %s", - ptv->buffer_size, devname, strerror(errno)); - close(ptv->socket); - return -1; - } - } - - r = bind(ptv->socket, (struct sockaddr *)&bind_address, sizeof(bind_address)); - if (r < 0) { - if (verbose) { - if (errno == ENETDOWN) { - SCLogError(SC_ERR_AFP_CREATE, - "Couldn't bind AF_PACKET socket, iface %s is down", - devname); - } else { - SCLogError(SC_ERR_AFP_CREATE, - "Couldn't bind AF_PACKET socket to iface %s, error %s", - devname, strerror(errno)); - } - } - close(ptv->socket); - return -1; - } + SCLogInfo("Using interface '%s' via socket %d", (char *)devname, ptv->socket); #ifdef HAVE_PACKET_FANOUT /* add binded socket to fanout group */ @@ -1145,8 +1263,7 @@ static int AFPCreateSocket(AFPThreadVars *ptv, char *devname, int verbose) SCLogError(SC_ERR_AFP_CREATE, "Coudn't set fanout mode, error %s", strerror(errno)); - close(ptv->socket); - return -1; + goto frame_err; } } #endif @@ -1162,14 +1279,23 @@ static int AFPCreateSocket(AFPThreadVars *ptv, char *devname, int verbose) rc = AFPSetBPFFilter(ptv); if (rc == TM_ECODE_FAILED) { SCLogError(SC_ERR_AFP_CREATE, "Set AF_PACKET bpf filter \"%s\" failed.", ptv->bpf_filter); - return -1; + goto frame_err; } /* Init is ok */ - ptv->afp_state = AFP_STATE_UP; - - AFPPeerUpdate(ptv); + AFPSwitchState(ptv, AFP_STATE_UP); return 0; + +frame_err: + if (ptv->frame_buf) + SCFree(ptv->frame_buf); +mmap_err: + /* Packet mmap does the cleaning when socket is closed */ +socket_err: + close(ptv->socket); + ptv->socket = -1; +error: + return -1; } TmEcode AFPSetBPFFilter(AFPThreadVars *ptv) @@ -1230,7 +1356,6 @@ TmEcode AFPSetBPFFilter(AFPThreadVars *ptv) */ TmEcode ReceiveAFPThreadInit(ThreadVars *tv, void *initdata, void **data) { SCEnter(); - int r; AFPIfaceConfig *afpconfig = initdata; if (initdata == NULL) { @@ -1310,24 +1435,25 @@ TmEcode ReceiveAFPThreadInit(ThreadVars *tv, void *initdata, void **data) { SCLogInfo("Enabling zero copy mode by using data release call"); } - r = AFPCreateSocket(ptv, ptv->iface, 1); - if (r < 0) { - SCLogError(SC_ERR_AFP_CREATE, "Couldn't init AF_PACKET socket"); + if (AFPPeersListAdd(ptv) == TM_ECODE_FAILED) { SCFree(ptv); afpconfig->DerefFunc(afpconfig); SCReturnInt(TM_ECODE_FAILED); } + r = AFPCreateSocket(ptv, ptv->iface, 1); + if (r < 0) { + SCLogError(SC_ERR_AFP_CREATE, "Couldn't init AF_PACKET socket"); + //SCFree(ptv); + //afpconfig->DerefFunc(afpconfig); + //SCReturnInt(TM_ECODE_OKFAILED); + } + ptv->copy_mode = afpconfig->copy_mode; if (ptv->copy_mode != AFP_COPY_MODE_NONE) { strlcpy(ptv->out_iface, afpconfig->out_iface, AFP_IFACE_NAME_LENGTH); ptv->out_iface[AFP_IFACE_NAME_LENGTH - 1]= '\0'; - if (AFPPeersListAdd(ptv) == TM_ECODE_FAILED) { - SCFree(ptv); - afpconfig->DerefFunc(afpconfig); - SCReturnInt(TM_ECODE_FAILED); - } } #define T_DATA_SIZE 70000 @@ -1374,6 +1500,8 @@ void ReceiveAFPThreadExitStats(ThreadVars *tv, void *data) { TmEcode ReceiveAFPThreadDeinit(ThreadVars *tv, void *data) { AFPThreadVars *ptv = (AFPThreadVars *)data; + AFPSwitchState(ptv, AFP_STATE_DOWN); + if (ptv->data != NULL) { SCFree(ptv->data); ptv->data = NULL; @@ -1382,8 +1510,6 @@ TmEcode ReceiveAFPThreadDeinit(ThreadVars *tv, void *data) { ptv->bpf_filter = NULL; - close(ptv->socket); - SCReturnInt(TM_ECODE_OK); } diff --git a/src/source-af-packet.h b/src/source-af-packet.h index 263b1d9105..5548b7d266 100644 --- a/src/source-af-packet.h +++ b/src/source-af-packet.h @@ -83,6 +83,7 @@ typedef struct AFPIfaceConfig_ typedef struct AFPPeer_ { char iface[AFP_IFACE_NAME_LENGTH]; SC_ATOMIC_DECLARE(int, socket); + SC_ATOMIC_DECLARE(int, sock_usage); SC_ATOMIC_DECLARE(int, if_idx); SC_ATOMIC_DECLARE(uint8_t, state); SCMutex sock_protect; @@ -96,7 +97,11 @@ typedef struct AFPPacketVars_ { void *relptr; int copy_mode; - AFPPeer *peer; + AFPPeer *peer; /**< Sending peer for IPS/TAP mode */ + /** Pointer to ::AFPPeer used for capture. Field is used to be able + * to do reference counting. + */ + AFPPeer *mpeer; } AFPPacketVars; /**