/* SPC300 bundle {{{ * * Copyright (C) 2009 Spidcom * * <<>> * * }}} */ /** * \file application/libmme/src/mme.c * \brief MME management library * \ingroup libmme * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libmme.h" static void mme_check_oui (mme_ctx_t *ctx, MME_t *mh, unsigned char *pkt) { /* Fill OUI information */ if (MMTYPE_IS_VS(mh->mmtype)) { /* Set MME_OUI_SPIDCOM only if it was not set before */ if ((memcmp (OUI_SPIDCOM, pkt + sizeof (MME_t), 3) == 0) && (ctx->oui != MME_OUI_NOT_SPIDCOM)) { ctx->oui = MME_OUI_SPIDCOM; } else /* the OUI check failed */ { /* One fragment of the MME (if fmi_fmsn != 0) did not ** contain SPiDCOM OUI, so the whole MME will be ** marked as a foreign VS MME */ ctx->oui = MME_OUI_NOT_SPIDCOM; } } } /** * Construct the FMI field of the MME header. * \param nf_mi number of fragments * \param fn_mi fragment number * \param fmsn fragmentation message sequence number * \return the FMI field. */ static inline unsigned short mme_fmi_set (uint nf_mi, uint fn_mi, uint fmsn) { return (fmsn << 8) | (nf_mi << 4) | fn_mi; } /** * Get the FMI field uncompressed from the MME header. * \param fmi the compressed FMI * \param nf_mi number of fragments * \param fn_mi fragment number * \param fmsn fragmentation message sequence number */ static inline void mme_fmi_get (const unsigned short fmi, uint *nf_mi, uint *fn_mi, uint *fmsn) { *fn_mi = fmi & 0xF; *nf_mi = (fmi >> 4) & 0xF; *fmsn = (fmi >> 8) & 0xFF; } /** * Create a MME message context. This context is used to build the MME message step by step.
* The provided buffer must be enough large to contain all the final payload.
* The MME payload can be bigger than an ethernet payload, as the fragmentation is managed by the 'send' function. * * \param ctx MME context to fill with init value * \param mmtype type of MME message (must be in official type list) * \param buffer the buffer to put the payload * \param length the buffer length * \return error type (MME_SUCCESS if success) */ mme_error_t mme_init (mme_ctx_t *ctx, const mmtype_t mmtype, unsigned char *buffer, const unsigned int length) { /* protect from null pointers */ if (ctx == NULL || buffer == NULL) return MME_ERROR_GEN; ctx->buffer = buffer; ctx->mmtype = mmtype; ctx->length = length; ctx->head = 0; ctx->tail = 0; /* By default the MME is not VS */ ctx->oui = MME_OUI_NOT_PRESENT; ctx->status = MME_STATUS_OK; return MME_SUCCESS; } /** Get the current MME payload length * * \param ctx MME context to get the payload length * \param length the returned length * \return error type (MME_SUCCESS if success) * \return MME_ERROR_NOT_INIT: context not initialized */ mme_error_t mme_get_length (mme_ctx_t *ctx, unsigned int *length) { /* protect from null pointers */ if (ctx == NULL || length == NULL) return MME_ERROR_GEN; /* check if ctx has been inititalized */ if (ctx->status == MME_STATUS_INIT) return MME_ERROR_NOT_INIT; *length = ctx->tail - ctx->head; return MME_SUCCESS; } /** Get the remaining free space to add payload * * \param ctx MME context to get the remaining space * \param length the remaining space * \return error type (MME_SUCCESS if success) * \return MME_ERROR_NOT_INIT: context not initialized */ mme_error_t mme_get_free_space (mme_ctx_t *ctx, unsigned int *length) { /* protect from null pointers */ if (ctx == NULL || length == NULL) return MME_ERROR_GEN; /* check if ctx has been inititalized */ if (ctx->status == MME_STATUS_INIT) return MME_ERROR_NOT_INIT; *length = ctx->length - (ctx->tail - ctx->head); return MME_SUCCESS; } /** * Push data at the beginning of the MME payload. Data are inserted between the start of buffer and the current payload data.
* MME data tail and length are updated.
* If there is not enough free place to push data, an error is returned and the remaining free space length is returned. * * \param ctx MME context where to push data * \param data data to be pushed into payload * \param length length of data to push * \param result_length length of data really pushed * \return error type (MME_SUCCESS if success) * \return MME_ERROR_NOT_INIT: context not initialized * \return MME_ERROR_SPACE: not enough available space */ mme_error_t mme_push (mme_ctx_t *ctx, const void *data, unsigned int length, unsigned int *result_length) { unsigned int free = 0; int delta = 0; /* protect from null pointers */ if (ctx == NULL || data == NULL || result_length == NULL) return MME_ERROR_GEN; /* check if ctx has been inititalized */ if (ctx->status == MME_STATUS_INIT) return MME_ERROR_NOT_INIT; free = ctx->length - (ctx->tail - ctx->head); if (length > free) { *result_length = free; return MME_ERROR_SPACE; } *result_length = length; /* make place in front, if needed */ if (length > ctx->head) { /* * *length * .-----------^-----------. * |---------|xxxxxxxxxxxxx|--------------------|---------------------| * buff head tail ctx->length * \_______________ _______________/ * \/ * pyload * * we have to shift right our payload for this difference delta marked with 'x' * in order for *length bytes to fit in from beginning of the buffer */ delta = length - ctx->head; memmove (ctx->buffer + ctx->head + delta, ctx->buffer + ctx->head, ctx->tail - ctx->head); /* update head and tail pointers (offsets) */ ctx->head += delta; ctx->tail += delta; } ctx->head -= length; /* place head pointer to where copy chould start from */ memcpy (ctx->buffer + ctx->head, data, length); return MME_SUCCESS; } /** * Put data at the end of MME payload. MME data tail and length are updated
* If there is not enough free place to put data, an error is returned and the remaining free space length is returned. * * \param ctx MME context where to put data * \param data data to put at the end of MME payload * \param length length of data to put * \param result_length length of data really put * \return error type (MME_SUCCESS if success) * \return MME_ERROR_NOT_INIT: context not initialized * \return MME_ERROR_SPACE: not enough available space */ mme_error_t mme_put (mme_ctx_t *ctx, const void *data, unsigned int length, unsigned int *result_length) { unsigned int free = 0; int delta = 0; /* protect from null pointers */ if (ctx == NULL || data == NULL || result_length == NULL) return MME_ERROR_GEN; /* check if ctx has been inititalized */ if (ctx->status == MME_STATUS_INIT) return MME_ERROR_NOT_INIT; free = ctx->length - (ctx->tail - ctx->head); if (length > free) { *result_length = free; return MME_ERROR_SPACE; } *result_length = length; /* make place after payload, if needed */ if (length > ctx->length - ctx->tail) { /* * *length * .---------------^-----------------. * |-----------|------------|xxxxxxx|-------------------------| * buff head tail ctx->length * \________ ________/ * \/ * payload * * we have to shift left our payload for this difference delta marked with 'x' * in order for *length bytes to fit in from beginning of the buffer */ delta = length - (ctx->length - ctx->tail); memmove ((unsigned char *) (ctx->buffer + ctx->head - delta), (unsigned char *) (ctx->buffer + ctx->head), ctx->tail - ctx->head); /* update head and tail pointers (offsets) */ ctx->head -= delta; ctx->tail -= delta; } memcpy ((unsigned char *) (ctx->buffer + ctx->tail), (unsigned char *) data, length); ctx->tail += length; return MME_SUCCESS; } /** * Pull (get) data from beginning of MME payload. MME data head and length are updated
* If there is not enough data to pull, an error is returned and the remaining payload length is returned. * * \param ctx MME context where to get data * \param data buffer where to get data from the beginning of MME payload * \param length length of data to pull * \param result_length length of data pulled; if there is not enough data into the MME to fit the length, the remaining data length is returned * \return error type (MME_SUCCESS if success) * \return MME_ERROR_NOT_INIT: context not initialized */ mme_error_t mme_pull (mme_ctx_t *ctx, void *data, unsigned int length, unsigned int *result_length) { /* protect from null pointers */ if (ctx == NULL || data == NULL || result_length == NULL) return MME_ERROR_GEN; /* check if ctx has been inititalized */ if (ctx->status == MME_STATUS_INIT) return MME_ERROR_NOT_INIT; /* check if it is demanded more data than we have in payload */ if (length > ctx->tail - ctx->head) { *result_length = ctx->tail - ctx->head; return MME_ERROR_ENOUGH; } *result_length = length; memcpy (data, (unsigned char *) (ctx->buffer + ctx->head), length); ctx->head += length; return MME_SUCCESS; } /** * Pop (get) data from the end of MME payload. MME data tail and length are updated
* If there is not enough data to pull, an error is returned and the remaining payload length is returned. * * \param ctx MME context where to get data * \param data buffer where to get data from the end of MME payload * \param length length of data to pull * \param result_length length of data gotten; if there is not enough data into the MME to fit the length, the remaining data length is returned * \return error type (MME_SUCCESS if success) * \return MME_ERROR_NOT_INIT: context not initialized * \return MME_ERROR_SPACE: not enough available space */ mme_error_t mme_pop (mme_ctx_t *ctx, void *data, unsigned int length, unsigned int *result_length) { /* protect from null pointers */ if (ctx == NULL || data == NULL || result_length == NULL) return MME_ERROR_GEN; /* check if ctx has been inititalized */ if (ctx->status == MME_STATUS_INIT) return MME_ERROR_NOT_INIT; /* check if it is demanded more data than we have in payload */ if (length > ctx->tail - ctx->head) { *result_length = ctx->tail - ctx->head; return MME_ERROR_ENOUGH; } *result_length = length; memcpy (data, (unsigned char *) (ctx->buffer + ctx->tail - length), length); ctx->tail -= length; return MME_SUCCESS; } static int _get_iface_mac_addr (int sock, char *iface, unsigned char *mac_addr) { struct ifreq ifr; memset (&ifr, 0x0, sizeof (ifr)); /* Get the interface Index */ strncpy (ifr.ifr_name, iface, IFNAMSIZ); if ((ioctl (sock, SIOCGIFINDEX, &ifr)) < 0) { return -1; } if (ioctl (sock, SIOCGIFHWADDR, &ifr) < 0) { return -1; } memcpy (mac_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); return 0; } /** * Build the MME header. It calculates the number of packet and returns it. * \param mh MME header, filled by this function * \param ctx MME context to send * \param src_mac MAC Address of the source * \param dest MAC Address of the destination * \return int The number of packets required to send the MME */ int mme_header_prepare (MME_t *mh, mme_ctx_t *ctx, unsigned char *src_mac, unsigned char *dest) { int nbpkt; uint fmi_nf_mi = 0; /* Number of fragments */ uint fmi_fmsn = 0; /* SSN of the fragmented mme */ uint payload_size; /* * --- Create MME header --- */ /* copy the Src mac addr */ memcpy (mh->mme_src, (void *)src_mac, 6); /* copy the Dst mac addr */ memcpy (mh->mme_dest, (void *)dest, 6); /* copy the protocol */ mh->mtype = htons (MME_TYPE); /* set default mme version */ mh->mmv = MME_VERSION; mh->mmtype = ctx->mmtype; /* Set the max payload size */ SET_MME_PAYLOAD_MAX_SIZE(mh->mmtype, payload_size); /* calculate number of mme packets needed */ nbpkt = (ctx->tail - ctx->head) / payload_size; if (nbpkt == 0 || ((ctx->tail - ctx->head) % payload_size) > 0) { nbpkt++; } fmi_nf_mi = nbpkt - 1; /* Set the sequence number to 1 if the message is fragmented. This number * correspond to a session number, all fragmented MME of this session must * have the same fmsn number. */ fmi_fmsn = fmi_nf_mi ? 1 : 0; mh->fmi = mme_fmi_set (fmi_nf_mi, 0, fmi_fmsn); return nbpkt; } /** * Build a packet from the context. This function will be called by mme_send * before sending each fragment of the packet. The function will update the * fragment offset. * This function is for internal use, and should not be used outside the * library. * \param mh MME header, will be updated with the correct fragment number * \param ctx MME context to send * \param fo Fragment offset, from which data must be sent * \param index Fragment number * \param pkt Pointer to the head of the data buffer * \return int Size of the packet (including header) */ int mme_prepare (MME_t *mh, mme_ctx_t *ctx, int *fo, int index, unsigned char *pkt) { uint fs = 0; uint fmi_nf_mi = 0; /* Number of fragments */ uint fmi_fn_mi = 0; /* Fragment number */ uint fmi_fmsn = 0; /* SSN of the fragmented mme */ uint payload_size, payload_min_size; SET_MME_PAYLOAD_MAX_SIZE(mh->mmtype, payload_size); SET_MME_PAYLOAD_MIN_SIZE(mh->mmtype, payload_min_size); /* * --- Set per-message fragmentation info, current fragment number --- */ mme_fmi_get (mh->fmi, &fmi_nf_mi, &fmi_fn_mi, &fmi_fmsn); mh->fmi = mme_fmi_set (fmi_nf_mi, index, fmi_fmsn); /* * --- Append payload --- */ /* In case of VS MME, append the OUI first */ if (MMTYPE_IS_VS(mh->mmtype)) { memcpy(pkt, OUI_SPIDCOM, SPIDCOM_OUI_SIZE); pkt += SPIDCOM_OUI_SIZE; } /* calculate the size of the current fragment */ fs = ((ctx->tail - *fo) < payload_size) ? (ctx->tail - *fo) : payload_size; /* copy the content into packet buffer */ memcpy (pkt, ctx->buffer + *fo, fs); /* update fo */ *fo += fs; /* zero-pad the rest if last packet fs < MME_MIN_SIZE, which is minimum size for mme packet */ if (fs < payload_min_size) { memset (pkt + fs, 0x0, payload_min_size - fs); fs = payload_min_size; } /* In case of VS MME, add SPIDCOM_OUI_SIZE to the payload */ if (MMTYPE_IS_VS(mh->mmtype)) { fs += SPIDCOM_OUI_SIZE; } return (fs + sizeof (MME_t)); } /** * Send MME to network interface and wait for confirm MME; this is a synchronous transmit and receive transaction * (function waits for answer MME before returning). If no answer packet is received before MME_CONFIRM_TIMEOUT, * error is returned.
* As MME are level 2 and IP routing does not apply, the network interface where to send data packet must be selected inside * iface paramater. For instance : "eth0", "br0", ...
* If an MME needs to be fragmented, the sending process is in charge to manage the fragmentation. The same as reassembling * the fragmented MME confirm.
* 3 types of communication can be done : *
  • MME_SEND_REQ_CNF : request and confirm transaction *
  • MME_SEND_CNF : confirm only (no answer expected) *
  • MME_SEND_IND_RSP : indicate and response transaction *
  • MME_SEND_IND : indicate only (no answer expected) *
* * \param ctx MME context to send * \param type transaction type * \param iface selected communication interface; if NULL, "br0" if remote destination, "lo" if local * \param dest destination MAC address in binary format (6 bytes) * \param confirm_ctx MME context given to put the received MME answer packet. A adapted buffer must have been provided. * \return error type (MME_SUCCESS if success) * \return MME_ERROR_NOT_INIT: context not initialized */ mme_error_t mme_send (mme_ctx_t *ctx, mme_send_type_t type, char *iface, unsigned char *dest, mme_ctx_t *confirm_ctx) { int raw; struct sockaddr_ll sll; struct ifreq ifr; MME_t *mh; unsigned char src_mac[6] = {0}; /* MAC of chosen interface */ unsigned char *pkt; unsigned char pkt_holder[ETH_PACKET_MAX_NOVLAN_SIZE] = {0}; int pkt_len; int sent; unsigned short nbpkt = 0; fd_set fds; uint mme_h_size; /* Size of MME header */ int fo; /* current fragment offset */ struct sockaddr_ll pkt_info; int last_pkt = 0; uint fmi_nf_mi = 0; /* Number of fragments */ uint fmi_fn_mi = 0; /* Fragment number */ uint fmi_fmsn = 0; /* SSN of the fragmented mme */ struct timeval timeout; /* When we should time out */ struct timeval now; struct timeval waittime; int res; int len; unsigned int result_len; int i; int pkt_nfo_sz = sizeof (pkt_info); mme_error_t ret; uint pkt_cnt = 0; char *iface_real; /* protect from null pointers */ if (ctx == NULL || confirm_ctx == NULL || dest == NULL) return MME_ERROR_GEN; /* check if ctx has been inititalized */ if (ctx->status == MME_STATUS_INIT) return MME_ERROR_NOT_INIT; /* * ##################### * # SEND * ##################### */ /* * --- Create the raw socket --- */ if ((raw = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_HPAV))) == -1) { perror ("Error creating raw socket: "); return MME_ERROR_GEN; } memset (&sll, 0x0, sizeof (sll)); memset (&ifr, 0x0, sizeof (ifr)); /* get the bridge mac-address */ _get_iface_mac_addr (raw, MME_IFACE_BRIDGE, src_mac); /* check for real interface to use */ if (NULL == iface) { /* let libmme guess the right interface */ if (!memcmp (src_mac, dest, ETH_ALEN)) { /* this is a local request */ iface_real = MME_IFACE_LOOPBACK; } else { /* this is a request to remote product */ iface_real = MME_IFACE_BRIDGE; } } else { /* keep the same interface */ iface_real = iface; } /* Get index of needed interface */ strncpy (ifr.ifr_name, iface_real, IFNAMSIZ); if ((ioctl (raw, SIOCGIFINDEX, &ifr)) == -1) { printf ("%s: Error getting %s index !\n", __FUNCTION__, iface_real); close (raw); return MME_ERROR_GEN; } /* Bind our raw socket to this interface */ sll.sll_family = AF_PACKET; sll.sll_ifindex = ifr.ifr_ifindex; sll.sll_protocol = htons (ETH_P_HPAV); if ((bind (raw, (struct sockaddr *)&sll, sizeof (sll)))== -1) { perror ("Error binding raw socket to interface\n"); close (raw); return MME_ERROR_GEN; } mh = (MME_t *)pkt_holder; nbpkt = mme_header_prepare (mh, ctx, src_mac, dest); mme_fmi_get (mh->fmi, &fmi_nf_mi, &fmi_fn_mi, &fmi_fmsn); /* Security check, if nbpkt is more than 15 don't send anything and inform * the user. */ if (fmi_nf_mi > 0xf) { printf("The MME to send is too long and cannot be fragmented\n"); return MME_ERROR_SPACE; } /* initialize fragment offset to the begining of the payload*/ fo = ctx->head; /* prepare and send out packets */ for (i=0; immtype, mme_h_size); /* Calculate into 'timeout' when we should time out */ gettimeofday (&timeout, 0); timeout.tv_sec += MME_TOUT; /* initialize packet counter */ pkt_cnt = 0; do /* receive the payload, packet by packet */ { /* rewind pkt pointer to begining of the packet holder */ pkt = pkt_holder; for (;;) /* try to receive until we get response from our proper server */ { /* Force timeout if we ran out of time */ gettimeofday (&now, 0); if (timercmp (&now, &timeout, >) != 0) { printf ("Server response timeout.\n"); close (raw); return MME_ERROR_TIMEOUT; } /* Calculate into 'waittime' how long to wait */ timersub (&timeout, &now, &waittime); /* wait = timeout - now */ /* Init fds with 'sock' as the only fd */ FD_ZERO (&fds); FD_SET (raw, &fds); /* fds fd_set is containing only our socket, so no need to check with FD_ISSET() */ res = select (raw + 1, &fds, NULL, NULL, &waittime); if (res < 0) { perror ("select() failed"); close (raw); return MME_ERROR_GEN; } else if (res == 0) /* timeout */ { printf ("Server response timeout.\n"); close (raw); return MME_ERROR_TIMEOUT; } if ((len = recvfrom (raw, pkt, ETH_PACKET_MAX_NOVLAN_SIZE, 0, (struct sockaddr*)&pkt_info, (socklen_t *)&pkt_nfo_sz)) < 0) { perror ("Recv from returned error: "); close (raw); return MME_ERROR_TXRX; } mh = (MME_t *)pkt; mme_fmi_get (mh->fmi, &fmi_nf_mi, &fmi_fn_mi, &fmi_fmsn); /* check if response came from the our proper server, and if it is, * check if correspondent response was sent (REQ/CNF or IND/RSP); * by HP AV standard difference is in LSB, i.e. if REQ LSBs are ...00, * CNF will be ...01, IND ...10 and RSP ...11 */ if (memcmp (pkt_info.sll_addr, dest, 6) == 0 /* we received packet from correct source */ && mh->mmtype == confirm_ctx->mmtype /* we received type that we expect */ && fmi_fn_mi == pkt_cnt ) /* packets are coming in the right order */ { mme_check_oui (confirm_ctx, mh, pkt); break; /* good answer - go out and process the package */ } } /* for */ /* check if the packet we received is the last one */ if (fmi_fn_mi == fmi_nf_mi) last_pkt = 1; /* skip the MME header */ pkt += mme_h_size; len -= mme_h_size; /* copy the packet into the receive buffer */ ret = mme_put (confirm_ctx, pkt, len, &result_len); if (ret != MME_SUCCESS) { close (raw); return ret; } pkt_cnt++; } while (!last_pkt); close (raw); return MME_SUCCESS; } /** * Wait for an MME request and call an user function to build and send the answer.
* As MME are level 2 and IP routing does not apply, the network interface where to listen to data packet must be selected inside * iface paramater. For instance : "eth0", "br0", ...
* If a received MME is fragmented, the receive process re-assemble it before delivering.
* When received MME has been processed and put into a MME context, the user function is called with the context in parameter. * * \param ctx context to receive the MME data * \param iface interface where to listen to the MME; if NULL, iface is guessed according to source * \param source buffer where is provided the source MAC address of received MME * \param listen_cb callback function used when MME packet is received. * \param tout timeout in seconds after which listening expires. * \return error type (MME_SUCCESS if success) * \return MME_ERROR_NOT_INIT: context not initialized */ mme_error_t mme_listen (mme_ctx_t *ctx, char *iface, unsigned char *source, mme_listen_cb_t listen_cb, unsigned int tout) { int sock_iface, sock_lo; struct sockaddr_ll sll; struct ifreq ifr; MME_t *mh; unsigned char *pkt; unsigned char pkt_holder[ETH_PACKET_MAX_NOVLAN_SIZE] = {0}; fd_set fds; struct sockaddr_ll pkt_info; int last_pkt = 0; uint fmi_nf_mi = 0; /* Number of fragments */ uint fmi_fn_mi = 0; /* Fragment number */ uint fmi_fmsn = 0; /* SSN of the fragmented mme */ struct timeval timeout; /* When we should time out */ struct timeval now; struct timeval waittime; int res; int len; uint mme_h_size; /* Size of MME header */ unsigned int result_len; int pkt_nfo_sz = sizeof (pkt_info); mme_error_t ret; uint pkt_cnt = 0; /* to indicate if we recive packets in * correct order */ /* protect from null pointers */ if (ctx == NULL || source == NULL) return MME_ERROR_GEN; /* check if ctx has been inititalized */ if (ctx->status == MME_STATUS_INIT) return MME_ERROR_NOT_INIT; /* Set the MME Header size */ SET_MME_HEADER_SIZE(ctx->mmtype, mme_h_size); /* * --- Create the iface socket --- */ if ((sock_iface = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_HPAV))) < 0) { perror ("Error creating sock_iface socket: "); return MME_ERROR_GEN; } memset (&sll, 0x0, sizeof (sll)); memset (&ifr, 0x0, sizeof (ifr)); if (NULL == iface) { strncpy (ifr.ifr_name, MME_IFACE_BRIDGE, IFNAMSIZ); } else { strncpy (ifr.ifr_name, iface, IFNAMSIZ); } /* get iface index */ if ((ioctl (sock_iface, SIOCGIFINDEX, &ifr)) == -1) { printf ("%s: Error getting %s index\n", __FUNCTION__, iface); close (sock_iface); return MME_ERROR_GEN; } /* Bind socket to this interface */ sll.sll_family = AF_PACKET; sll.sll_ifindex = ifr.ifr_ifindex; sll.sll_protocol = htons (ETH_P_HPAV); if ((bind (sock_iface, (struct sockaddr *)&sll, sizeof (sll)))== -1) { perror ("Error binding iface to interface: "); close (sock_iface); return MME_ERROR_GEN; } /* create loopback socket */ if ((sock_lo = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_HPAV))) < 0) { perror ("Error creating loopback socket: "); close (sock_iface); return MME_ERROR_GEN; } memset (&sll, 0x0, sizeof (sll)); memset (&ifr, 0x0, sizeof (ifr)); if (NULL == iface) { /* Get the loopback Interface Index */ strncpy (ifr.ifr_name, MME_IFACE_LOOPBACK, IFNAMSIZ); /* get iface index */ if ((ioctl (sock_lo, SIOCGIFINDEX, &ifr)) == -1) { perror ("Error getting loopback index: "); close (sock_iface); close (sock_lo); return MME_ERROR_GEN; } /* Bind socket to this interface */ sll.sll_family = AF_PACKET; sll.sll_ifindex = ifr.ifr_ifindex; sll.sll_protocol = htons (ETH_P_HPAV); if ((bind (sock_lo, (struct sockaddr *)&sll, sizeof (sll)))== -1) { perror ("Error binding loopback socket to interface\n"); close (sock_iface); close (sock_lo); return MME_ERROR_GEN; } } /* Calculate into 'timeout' when we should time out */ gettimeofday (&timeout, 0); timeout.tv_sec += (tout != 0) ? tout : MME_TOUT; /* initialize packet counter */ pkt_cnt = 0; do /* receive the payload packages */ { /* rewind pkt pointer to begining of the packet holder */ pkt = pkt_holder; for(;;) /* listen until we get package from our proper source */ { /* Force timeout if we ran out of time */ gettimeofday (&now, 0); if (timercmp (&now, &timeout, >) != 0) { printf ("Server indication timeout.\n"); close (sock_iface); close (sock_lo); return MME_ERROR_TIMEOUT; } /* Calculate into 'waittime' how long to wait */ timersub (&timeout, &now, &waittime); /* wait = timeout - now */ /* Init fds with 'sock' as the only fd */ FD_ZERO (&fds); FD_SET (sock_iface, &fds); if (NULL == iface) { FD_SET (sock_lo, &fds); } res = select (sock_iface + 2, &fds, NULL, NULL, &waittime); if (res < 0) { perror ("select() failed"); close (sock_iface); close (sock_lo); return MME_ERROR_GEN; } else if (res == 0) /* timeout */ { printf ("Server indication timeout.\n"); close(sock_iface); close(sock_lo); return MME_ERROR_TIMEOUT; } if (FD_ISSET (sock_iface, &fds)) { if ((len = recvfrom (sock_iface, pkt, ETH_PACKET_MAX_NOVLAN_SIZE, 0, (struct sockaddr*)&pkt_info, (socklen_t *)&pkt_nfo_sz) ) < 0 ) { perror ("Recv from returned error: "); close (sock_iface); close (sock_lo); return MME_ERROR_TXRX; } } else { if ((len = recvfrom (sock_lo, pkt, ETH_PACKET_MAX_NOVLAN_SIZE, 0, (struct sockaddr*)&pkt_info, (socklen_t *)&pkt_nfo_sz) ) < 0 ) { perror ("Recv from loopback returned error: "); close (sock_iface); close (sock_lo); return MME_ERROR_TXRX; } } mh = (MME_t *)pkt; mme_fmi_get (mh->fmi, &fmi_nf_mi, &fmi_fn_mi, &fmi_fmsn); /* check if indication came from the good source, and if it is, * check if cooresponds to our prepared response (REQ/CNF or IND/RSP); * by HP AV standard difference is in LSB, i.e. if REQ LSBs are ...00, * CNF will be ...01, IND ...10 and RSP ...11 * REQ is always CNF - 1, IND is RSP - 1 */ if (memcmp (pkt_info.sll_addr, source, ETH_ALEN) == 0 /* we received packet from correct source */ && (mh->mmtype == ctx->mmtype) /* waited MME type */ && fmi_fn_mi == pkt_cnt) /* packets are coming in the right order */ { mme_check_oui (ctx, mh, pkt); break; /* good answer - go out and process the package */ } } /* for */ /* check if the packet we received is the last one */ if (fmi_fn_mi == fmi_nf_mi) last_pkt = 1; /* skip the MME header */ pkt += mme_h_size; len -= mme_h_size; /* copy the packet into the receive buffer */ ret = mme_put (ctx, pkt, len, &result_len); if (ret != MME_SUCCESS) { close (sock_iface); close (sock_lo); return ret; } pkt_cnt++; } while (!last_pkt); /* and now whe we assembled the message, call the processing function */ if (listen_cb != NULL) { ret = listen_cb (ctx, iface, source); if (ret != MME_SUCCESS) { close (sock_iface); close (sock_lo); return ret; } } close (sock_iface); close (sock_lo); return MME_SUCCESS; }