/* * $Id: adaptation.c,v 1.13 2001/03/05 10:03:32 ajung Exp $ * * SCTP implementation according to RFC 2960. * Copyright (C) 2000 by Siemens AG, Munich, Germany. * * Realized in co-operation between Siemens AG * and University of Essen, Institute of Computer Networking Technology. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * There are two mailinglists available at http://www.sctp.de which should * be used for any discussion related to this implementation. * * Contact: discussion AT sctp.de * Michael.Tuexen AT icn.siemens.de * ajung AT exp-math.uni-essen.de * * Purpose: The adaption-module encapsulates the socket-interface. * The adaption-module also handles timers in the SCTP and its ULP. */ #include "adaptation.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <netinet/in_systm.h> #include <netinet/ip.h> #ifdef HAVE_IPV6 #ifdef LINUX_IPV6 #include <netinet/ip6.h> #else /* include files for IPv6 header structs */ #endif #endif #include <netdb.h> #include <arpa/inet.h> /* for inet_ntoa() under both SOLARIS/LINUX */ #include <sys/errno.h> #include <errno.h> #include "timer_list.h" #ifdef HAVE_SYS_POLL_H #include <sys/poll.h> #else #define POLLIN 0x001 #define POLLPRI 0x002 #define POLLOUT 0x004 #define POLLERR 0x008 struct pollfd { int fd; short int events; short int revents; }; #endif #ifndef HAVE_POLL /* a simple poll implementation based on select */ int poll(struct pollfd *fdlist, long unsigned int count, int time) { struct timeval timeout, *to; fd_set readfdset, writefdset; long unsigned int i; int ret, fdcount = 0; int tsize = getdtablesize(); if (time < 0) to = NULL; else { to = &timeout; timeout.tv_sec = time / 1000; timeout.tv_usec = (time % 1000) * 1000; } FD_ZERO(&readfdset); FD_ZERO(&writefdset); for (i = 0; i < count; i++) { if (fdlist[i].fd < 0) continue; if (fdlist[i].events & POLLIN) FD_SET(fdlist[i].fd, &readfdset); if (fdlist[i].events & POLLOUT) FD_SET(fdlist[i].fd, &writefdset); fdcount++; } if (fdcount == 0) return(0); for (i = 0; i < count; i++) fdlist[i].revents = 0; if ((ret = select(tsize, &readfdset, &writefdset, NULL, to)) == -1) return(-1); for (i = 0; i < count; i++) { if (FD_ISSET(fdlist[i].fd, &readfdset) && (fdlist[i].events & POLLIN)) fdlist[i].revents |= POLLIN; if (FD_ISSET(fdlist[i].fd, &writefdset) && (fdlist[i].events & POLLOUT)) fdlist[i].revents |= POLLOUT; } return(ret); } #endif #define POLL_FD_UNUSED -1 #define NUM_FDS 20 /** * Structure for callback events. The function "action" is called by the event-handler, * when a socket gets data or the ULP is to be notified of some conditions. * The function (action) takes five arguments : * 1. socket fd, where event occurred * 2. pointer to a (static) buffer where the data sits * 3. an integer that contains the length of the data * 4. pointer to the address where data came from * @param size of the address/sockaddr_in, the origin of the message */ struct event_cb { int sfd; int event_type; /* pointer to possible arguments, associations etc. */ void *arg1; void *arg2; void (*action) (int, unsigned char *, int, unsigned char[], unsigned short); }; /* a static counter - for stats we should have more counters ! */ static unsigned int number_of_sendevents = 0; /* a static receive buffer */ static unsigned char rbuf[MAX_MTU_SIZE + 20]; static struct pollfd poll_fds[NUM_FDS]; static int num_of_fds = 0; static int sctp_sfd = 0; /* socket fd for standard SCTP port.... */ #ifdef HAVE_IPV6 static int sctpv6_sfd = 0; #endif /* will be added back later.... static int icmp_sfd = 0; */ /* socket fd for ICMP messages */ static struct event_cb *event_callbacks[NUM_FDS]; /** * converts address-string (hex for ipv6, dotted decimal for ipv4 * to a sockunion structure * @return 0 for success, else -1. */ int adl_str2sockunion(guchar * str, union sockunion *su) { int ret; memset(su, 0, sizeof(union sockunion)); ret = inet_aton(str, &su->sin.sin_addr); if (ret > 0) { /* Valid IPv4 address format. */ su->sin.sin_family = AF_INET; #ifdef HAVE_SIN_LEN su->sin.sin_len = sizeof(struct sockaddr_in); #endif /* HAVE_SIN_LEN */ return 0; } #ifdef HAVE_IPV6 ret = inet_pton(AF_INET6, str, &su->sin6.sin6_addr); if (ret > 0) { /* Valid IPv6 address format. */ su->sin6.sin6_family = AF_INET6; #ifdef SIN6_LEN su->sin6.sin6_len = sizeof(struct sockaddr_in6); #endif /* SIN6_LEN */ return 0; } #endif /* HAVE_IPV6 */ return -1; } const int adl_sockunion2str(union sockunion *su, guchar * buf, size_t len) { if (su->sa.sa_family == AF_INET){ if (len > 16) len = 16; strncpy(buf, inet_ntoa(su->sin.sin_addr), len); return(1); } #ifdef HAVE_IPV6 else if (su->sa.sa_family == AF_INET6) { event_log(VERBOSE, "TODO: check or change function for IPv6 address conversion !"); if (inet_ntop(AF_INET6, &su->sin6.sin6_addr, buf, len)==NULL) return 0; return (1); } #endif /* HAVE_IPV6 */ return 0; } boolean adl_equal_address(union sockunion * one, union sockunion * two) { #ifdef HAVE_IPV6 unsigned int count; #endif switch (sockunion_family(one)) { case AF_INET: if (sockunion_family(two) != AF_INET) return FALSE; return (sock2ip(one) == sock2ip(two)); break; #ifdef HAVE_IPV6 case AF_INET6: if (sockunion_family(two) != AF_INET6) return FALSE; for (count = 0; count < 16; count++) if (sock2ip6(one)[count] != sock2ip6(two)[count]) return FALSE; return TRUE; break; #endif default: error_logi(ERROR_MAJOR, "Address family %d not supported", sockunion_family(one)); return FALSE; break; } } gint adl_open_sctp_socket(int af) { struct protoent *proto; int sfd, protoid = 0, ch; if (!(proto = getprotobyname("sctp"))) { if (Current_error_log_ >= ERROR_WARNING) error_log(ERROR_MINOR, "SCTP: unknown protocol sctp - using 132\n"); protoid = IPPROTO_SCTP; } if (!protoid) protoid = proto->p_proto; if ((sfd = socket(af, SOCK_RAW, protoid)) < 0) { if (errno == EPERM) { if (Current_error_log_ >= ERROR_WARNING) error_log(ERROR_FATAL, "SCTP: must run as root or setuid root\n"); } else error_log(ERROR_MAJOR, "SCTP: socket creation failed in adl_open_sctp_socket !"); } ch = 0; switch (af) { case AF_INET: if (setsockopt(sfd, IPPROTO_IP, IP_HDRINCL, (char *) &ch, sizeof(ch))< 0) { error_log(ERROR_FATAL, "setsockopt: IP_HDRINCL"); } #if defined (LINUX) ch = IP_PMTUDISC_DO; if (setsockopt(sfd, IPPROTO_IP, IP_MTU_DISCOVER, (char *) &ch, sizeof(ch)) < 0) { error_log(ERROR_FATAL, "setsockopt: IP_PMTU_DISCOVER"); } #else error_log(ERROR_MINOR, "TODO : PATH MTU Discovery Disabled For Now !!!"); #endif break; #ifdef HAVE_IPV6 case AF_INET6: /* ch = 0; */ /* if (setsockopt(sfd, SOL_IPV6, IP_HDRINCL, (char *) &ch, sizeof(ch))< 0) { error_log(ERROR_FATAL, "setsockopt: IPv6_HDRINCL"); } */ event_log(VERBOSE,"IPv6 specific socket options must be set later, too !"); /* break; */ #endif default: error_log(ERROR_MINOR, "TODO : PATH MTU Discovery Disabled For Now !!!"); break; } event_logi(INTERNAL_EVENT_0, "Created raw socket %d with options\n", sfd); return (sfd); } gint adl_get_sctpv4_socket(void) { /* this is a static variable ! */ return sctp_sfd; } #ifdef HAVE_IPV6 gint adl_get_sctpv6_socket(void) { /* this is a static variable ! */ return sctpv6_sfd; } #endif /** * This function creates a UDP socket bound to localhost, for asynchronous * interprocess communication with an Upper Layer process. * @return the socket file descriptor. Used to register a callback function */ int adl_open_udp_socket(union sockunion* me) { guchar buf[1000]; int ch, sfd; switch (sockunion_family(me)) { case AF_INET: if ((sfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { error_log(ERROR_FATAL, "SCTP: socket creation failed for UDP socket !"); } ch = bind(sfd, (struct sockaddr *) &me, sizeof(struct sockaddr_in)); adl_sockunion2str(me, buf, SCTP_MAX_IP_LEN); event_logiii(VERBOSE, " adl_open_udp_socket : Create socket %u, binding to address %s, result %d",sfd, buf, ch); if (ch == 0) return (sfd); return -1; break; #ifdef HAVE_IPV6 case AF_INET6: if ((sfd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { error_log(ERROR_FATAL, "SCTP: socket creation failed for UDPv6 socket"); } ch = bind(sfd, (struct sockaddr *) &me, sizeof(struct sockaddr_in6)); adl_sockunion2str(me, buf, SCTP_MAX_IP_LEN); event_logiii(VERBOSE, " adl_open_udp_socket : Create socket %u, binding to address %s, result %d",sfd, buf, ch); if (ch == 0) return (sfd); return -1; break; #endif default: return -1; break; } } /** * function to be called when we get a message from a peer sctp instance in the poll loop * @param sfd the socket file descriptor where data can be read... * @param buf pointer to a buffer, where we data is stored * @param len number of bytes to be sent, including the ip header ! * @param address, where data goes from * @param dest_len size of the address * @return returns number of bytes actually sent, or error */ int sctp_sendUdpData(int sfd, unsigned char* buf, int length, unsigned char destination[], unsigned short dest_port) { union sockunion dest_su; int dest_len; int result; if ((sfd == sctp_sfd) || (sfd == 0) #ifdef HAVE_IPV6 || (sfd == sctpv6_sfd) #endif ) { error_log(ERROR_MAJOR, "You are trying to send UDP data to an SCTP socket or STDIN"); return -1; } result = adl_str2sockunion(destination, &dest_su); if (result != 0) { error_logi(ERROR_MAJOR, "Invalid destination address in sctp_sendUdpData(%s)",destination); return -1; } if (buf == NULL) { error_log(ERROR_MAJOR, "Invalid buffer sctp_sendUdpData()"); return -1; } if (dest_port == 0) { error_log(ERROR_MAJOR, "Invalid port in sctp_sendUdpData()"); return -1; } switch (sockunion_family(&dest_su)) { case AF_INET: dest_su.sin.sin_port = htons(dest_port); dest_len = sizeof(struct sockaddr_in); result = sendto(sfd, buf, length, 0, (struct sockaddr *) &(dest_su.sin), dest_len); break; #ifdef HAVE_IPV6 case AF_INET6: dest_su.sin6.sin6_port = htons(dest_port); dest_len = sizeof(struct sockaddr_in6); result = sendto(sfd, buf, length, 0, (struct sockaddr *) &(dest_su.sin6), dest_len); break; #endif default : error_logi(ERROR_MAJOR, "Invalid address family in sctp_sendUdpData(%s)",destination); result = -1; break; } return result; } /** * function to be called when library sends a message on an SCTP socket * @param sfd the socket file descriptor where data will be sent * @param buf pointer to a buffer, where data to be sent is stored * @param len number of bytes to be sent * @param destination address, where data is to be sent * @param dest_len size of the address * @return returns number of bytes actually sent, or error */ int adl_send_message(int sfd, void *buf, int len, union sockunion *dest, unsigned char tos) { int txmt_len = 0, dest_len, tmp, opt_len; unsigned char old_tos; #ifdef HAVE_IPV6 guchar hostname[MAX_MTU_SIZE]; #endif switch (sockunion_family(dest)) { case AF_INET: opt_len = sizeof(unsigned char); tmp = getsockopt(sfd, SOL_IP, IP_TOS, &old_tos, &opt_len); number_of_sendevents++; tmp = setsockopt(sfd, SOL_IP, IP_TOS, &tos, sizeof(unsigned char)); event_logiiii(VERBOSE, "AF_INET : adl_send_message : sfd : %d, len %d, destination : %s, send_events %u", sfd, len, inet_ntoa(dest->sin.sin_addr), number_of_sendevents); dest_len = sizeof(struct sockaddr_in); txmt_len = sendto(sfd, buf, len, 0, (struct sockaddr *) &(dest->sin), dest_len); if (txmt_len < 0) { perror("AF_INET : Sendto returned error : "); error_logi(ERROR_MAJOR, "AF_INET : sendto()=%d, retrying !", txmt_len); txmt_len = sendto(sfd, buf, len, 0, (struct sockaddr *) &(dest->sin), dest_len); error_logi(ERROR_MAJOR, "AF_INET : Second sendto() returned %d !", txmt_len); } tmp = setsockopt(sfd, SOL_IP, IP_TOS, &old_tos, sizeof(unsigned char)); break; #ifdef HAVE_IPV6 case AF_INET6: number_of_sendevents++; inet_ntop(AF_INET6, sock2ip6(dest), hostname, MAX_MTU_SIZE); event_logiiii(VERBOSE, "AF_INET6: adl_send_message : sfd : %d, len %d, destination : %s, send_events: %u", sfd, len, hostname, number_of_sendevents); dest_len = sizeof(struct sockaddr_in6); txmt_len = sendto(sfd, buf, len, 0, &(dest->sin6), dest_len); if (txmt_len < 0) { perror("AF_INET6 : Sendto returned error : "); error_logi(ERROR_MAJOR, "AF_INET6 : sendto()=%d, retrying !", txmt_len); txmt_len = sendto(sfd, buf, len, 0, &(dest->sin6), dest_len); error_logi(ERROR_MAJOR, "AF_INET6 : Second sendto() returned %d !", txmt_len); } break; #endif default: error_logi(ERROR_MAJOR, "adl_send_message : Adress Family %d not supported here", sockunion_family(dest)); txmt_len = -1; } return txmt_len; } /** * function to assign an event mask to a certain poll */ void assign_poll_fd(int fd_index, int sfd, int event_mask) { if (fd_index > NUM_FDS) error_log(ERROR_FATAL, "FD_Index bigger than NUM_FDS ! bye !\n"); poll_fds[fd_index].fd = sfd; /* file descriptor */ poll_fds[fd_index].events = event_mask; } /** * remove a sfd from the poll_list, and shift that list to the left * @return number of sfd's removed... */ int adl_remove_poll_fd(gint sfd) { int i, tmp, counter = 0; for (i = 0, tmp = 0; i < NUM_FDS; i++, tmp++) { if (tmp < NUM_FDS) { poll_fds[i].fd = poll_fds[tmp].fd; poll_fds[i].events = poll_fds[tmp].events; event_callbacks[i] = event_callbacks[tmp]; } else { poll_fds[i].fd = POLL_FD_UNUSED; poll_fds[i].events = 0; free(event_callbacks[i]); event_callbacks[i] = NULL; } if (poll_fds[i].fd == sfd) { tmp = i + 1; if (tmp < NUM_FDS) { poll_fds[i].fd = poll_fds[tmp].fd; poll_fds[i].events = poll_fds[tmp].events; event_callbacks[i] = event_callbacks[tmp]; } else { poll_fds[i].fd = POLL_FD_UNUSED; poll_fds[i].events = 0; free(event_callbacks[i]); event_callbacks[i] = NULL; } counter++; num_of_fds -= 1; } } return (counter); } /** * function to register a file descriptor, that gets activated for certain read/write events * when these occur, the specified callback funtion is activated and passed the parameters * that are pointed to by the event_callback struct */ int adl_register_fd_cb(int sfd, int event_mask, void (*action) (int, unsigned char *, int, unsigned char[], unsigned short)) { if (num_of_fds < NUM_FDS) { assign_poll_fd(num_of_fds, sfd, event_mask); event_callbacks[num_of_fds] = malloc(sizeof(struct event_cb)); if (!event_callbacks[num_of_fds]) error_log(ERROR_FATAL, "Could not allocate memory in register_fd_cb \n"); event_callbacks[num_of_fds]->sfd = sfd; event_callbacks[num_of_fds]->event_type = 0; event_callbacks[num_of_fds]->action = action; num_of_fds++; return num_of_fds; } else return (-1); } /** * function to be called when we get a message from a peer sctp instance in the poll loop * @param sfd the socket file descriptor where data can be read... * @param dest pointer to a buffer, where we can store the received data * @param maxlen maximum number of bytes that can be received with call * @param address, where we got the data from * @param from_len size of the address * @return returns number of bytes received with this call */ int adl_get_message(int sfd, void *dest, int maxlen, union sockunion *from, int *from_len) { int len; len = recvfrom(sfd, dest, maxlen, 0, (struct sockaddr *) from, from_len); if (len < 0) error_log(ERROR_FATAL, "recvfrom failed in get_message(), aborting !"); return len; } /** * this function is responsible for calling the callback functions belonging * to all of the file descriptors that have indicated an event ! * TODO : check handling of POLLERR situation * TODO : check handling of several read events at once * @param num_of_events number of events indicated by poll() */ void dispatch_event(int num_of_events) { int i = 0, j = 0; int length=0, src_len; union sockunion src; struct sockaddr_in *src_in; guchar src_address[SCTP_MAX_IP_LEN]; unsigned short portnum=0; #if !defined (LINUX) struct ip *iph; #else struct iphdr *iph; #endif #ifdef HAVE_IPV6 struct ip6_hdr *ip6h; #endif int hlen; if (num_of_events > 1) error_log(ERROR_MINOR, "Several events in one poll !"); for (j = 0; j < num_of_events; j++) { for (i = 0; i < num_of_fds; i++) { if (poll_fds[i].revents) { if (poll_fds[i].revents & POLLERR) { /* We must have specified this callback funtion for treating/logging the error */ error_log(ERROR_MINOR, "Poll Error Condition "); hlen = 0; length = 0; src_len = 0; (*(event_callbacks[i]->action)) (poll_fds[i].fd, &rbuf[hlen], length, NULL, src_len); break; } src_len = sizeof(src); if ((poll_fds[i].revents & POLLPRI) || (poll_fds[i].revents & POLLIN)) { if ( ((sctp_sfd !=0) && (poll_fds[i].fd != sctp_sfd)) #ifdef HAVE_IPV6 && ((sctpv6_sfd != 0) && (poll_fds[i].fd != sctpv6_sfd)) #endif ) { /* then it must either be STDIN or a UDP callback */ if (poll_fds[i].fd == 0) { src_len = 0; portnum = 0; event_logi(VERBOSE, "Message %d bytes - Activating STDIN callback", length); (*(event_callbacks[i]->action)) (0, rbuf, length, NULL, portnum); }else { length = adl_get_message(poll_fds[i].fd, rbuf, MAX_MTU_SIZE, &src, &src_len); event_logi(VERBOSE, "Message %d bytes - Activating UDP callback", length); adl_sockunion2str(&src, src_address, SCTP_MAX_IP_LEN); switch (sockunion_family(&src)) { case AF_INET : portnum = ntohs(src.sin.sin_port); break; #ifdef HAVE_IPV6 case AF_INET6: portnum = ntohs(src.sin6.sin6_port); break; #endif default: portnum = 0; break; } (*(event_callbacks[i]->action)) (poll_fds[i].fd, rbuf, length, src_address, portnum); } } else { length = adl_get_message(poll_fds[i].fd, rbuf, MAX_MTU_SIZE, &src, &src_len); event_logiii(VERBOSE, "SCTP-Message on socket %u , len=%d, portnum=%d", poll_fds[i].fd, length, portnum); switch (sockunion_family(&src)) { case AF_INET: src_in = (struct sockaddr_in *) &src; src_len = sizeof(struct sockaddr_in); event_logi(VERBOSE, "IPv4/SCTP-Message from %s -> activating callback", inet_ntoa(src_in->sin_addr)); #if !defined (LINUX) iph = (struct ip *) rbuf; hlen = iph->ip_hl << 2; #else iph = (struct iphdr *) rbuf; hlen = iph->ihl << 2; #endif if (length < hlen) { error_logii(ERROR_MINOR, "dispatch_event : packet too short (%d bytes) from %s", length, inet_ntoa(src_in->sin_addr)); } else { length -= hlen; adl_sockunion2str(&src, src_address, SCTP_MAX_IP_LEN); (*(event_callbacks[i]->action)) (poll_fds[i].fd, &rbuf[hlen], length, src_address, 0); } break; #ifdef HAVE_IPV6 case AF_INET6: adl_sockunion2str(&src, src_address, SCTP_MAX_IP_LEN); /* if we have additional options, we must parse them, and deduct the sizes :-( */ event_logii(VERBOSE, "IPv6/SCTP-Message from %s (%d bytes) -> activating callback", src_address, length); (*(event_callbacks[i]->action)) (poll_fds[i].fd, rbuf, length, src_address, 0); break; #endif /* HAVE_IPV6 */ default: error_logi(ERROR_MAJOR, "Unsupported Address Family Type ", sockunion_family(&src)); break; } } /* else */ } } /* if (poll_fds[i].revents) */ poll_fds[i].revents = 0; } /* for(i = 0; i < num_of_fds; i++) */ } /* for(j = 0; i < num_of_events; j++) */ } /** * function calls the respective callback funtion, that is to be executed as a timer * event, passing it two arguments */ void dispatch_timer(void) { int tid, result; AlarmTimer event; if (timer_list_empty()) return; result = get_msecs_to_nexttimer(); if (result == 0) { result = get_next_event(&event); if (!result) error_log(ERROR_FATAL, "get_next_event did not return 0\n"); tid = event.timer_id; (*(event.action)) (tid, event.arg1, event.arg2); result = remove_item(tid, &event); if (result) error_log(ERROR_FATAL, "remove_item did not return 0\n"); } return; } void adl_add_msecs_totime(struct timeval *t, unsigned int msecs) { long seconds = 0, microseconds = 0; struct timeval tmp, res; seconds = msecs / 1000; microseconds = (msecs % 1000) * 1000; tmp.tv_sec = seconds; tmp.tv_usec = microseconds; timeradd(t, &tmp, &res); memcpy(t, &res, sizeof(res)); return; } /** * helper function for the sake of a cleaner interface :-) */ int adl_gettime(struct timeval *tv) { return (gettimeofday(tv, (struct timezone *) NULL)); } /** * function is to return difference in msecs between time a and b (i.e. a-b) * @param a later time (e.g. current time) * @param b earlier time * @return -1 if a is earlier than b, else msecs that passed from b to a */ int adl_timediff_to_msecs(struct timeval *a, struct timeval *b) { struct timeval result; int retval; /* result = a-b */ timersub(a, b, &result); retval = result.tv_sec * 1000 + result.tv_usec / 1000; event_logi(VVERBOSE, "Computed Time Difference : %d msecs", retval); return ((retval < 0) ? -1 : retval); } /** * function initializes the array of fds we want to use for listening to events * USE POLL_FD_UNUSED to differentiate between used/unused fds ! */ int init_poll_fds(void) { int i; for (i = 0; i < NUM_FDS; i++) { assign_poll_fd(i, POLL_FD_UNUSED, 0); } num_of_fds = 0; return (0); } /** * function to check for events on all poll fds (i.e. open sockets), or else * execute the next timer event. Executed timer events are removed from the list. * Wrapper to poll() -- returns after timeout or read event * @return number of events that where seen on the socket fds, 0 for timer event, -1 for error * @author ajung */ int sctp_eventLoop(void) { int result; unsigned int u_res; int msecs; msecs = get_msecs_to_nexttimer(); /* returns -1 if no timer in list */ /* if (msecs > GRANULARITY || msecs < 0) */ if (msecs < 0) msecs = GRANULARITY; if (msecs == 0) { dispatch_timer(); return (0); } /* print_debug_list(INTERNAL_EVENT_0); */ result = poll(poll_fds, num_of_fds, msecs); switch (result) { case -1: error_log(ERROR_MAJOR, "Poll returned error, aborting !"); return -1; break; case 0: dispatch_timer(); break; default: u_res = (unsigned int) result; event_logi(INTERNAL_EVENT_0, "############### %d Read Event(s) occurred -> dispatch_event()#############", u_res); dispatch_event(result); break; } return (result); } /** * function to check for events on all poll fds (i.e. open sockets), or else * execute the next timer event. Executed timer events are removed from the list. * Wrapper to poll() -- returns at once or after a read event * @return 0 after shutdown of the application, -1 for error * @author ajung */ int sctp_getEvents(void) { int result; unsigned int u_res; int msecs; msecs = get_msecs_to_nexttimer(); /* returns -1 if no timer in list */ if (msecs == 0) { dispatch_timer(); return (0); } result = poll(poll_fds, num_of_fds, 0); switch (result) { case -1: error_log(ERROR_MAJOR, "Poll returned error, aborting !"); return -1; break; case 0: event_log(VVERBOSE, "Poll returned 0 events, returning !"); return 0; break; default: u_res = (unsigned int) result; event_logi(INTERNAL_EVENT_0, "############### %d Read Event(s) occurred -> dispatch_event()#############", u_res); dispatch_event(result); break; } return (result); } void adl_init_adaptation_layer() { init_poll_fds(); init_timer_list(); /* print_debug_list(INTERNAL_EVENT_0); */ sctp_sfd = adl_open_sctp_socket(AF_INET); /* we should - in a later revision - add back the a function that opens appropriate ICMP sockets (IPv4 and/or IPv6) and registers these with callback functions that also set PATH MTU correctly */ #ifdef HAVE_IPV6 /* icmpv6_sfd = int adl_open_icmpv6_socket(); */ sctpv6_sfd = adl_open_sctp_socket(AF_INET6); /* adl_register_socket_cb(icmpv6_sfd, adl_icmpv6_cb); */ #endif /* icmp_sfd = int adl_open_icmp_socket(); */ /* adl_register_socket_cb(icmp_sfd, adl_icmp_cb); */ /* #if defined(HAVE_SETUID) && defined(HAVE_GETUID) */ /* now we could drop privileges, if we did not use setsockopt() calls for IP_TOS etc. later */ /* setuid(getuid()); */ /* #endif */ return; } /** * this function is supposed to open and bind a UDP socket listening on a port * to incoming udp pakets on a local interface (a local union sockunion address) * @param me pointer to a local address, that will trigger callback, if it receives UDP data * @param scf callback funtion that is called when data has arrived * @return new UDP socket file descriptor, or -1 if error ocurred */ int sctp_registerUdpCallback(unsigned char me[], unsigned short my_port, sctp_socketCallback scf) { int result, new_sfd; union sockunion my_address; if (ntohs(my_port) == 0) { error_log(ERROR_MAJOR, "Port 0 is not allowed ! Fix your program !"); return -1; } if (adl_str2sockunion(me, &my_address) < 0) { error_logi(ERROR_MAJOR, "Could not convert address string %s !", me); return -1; } switch (sockunion_family(&my_address)) { case AF_INET: event_logi(VERBOSE, "Registering ULP-Callback for UDP socket on port %u",ntohs(my_port)); my_address.sin.sin_port = htons(my_port); break; #ifdef HAVE_IPV6 case AF_INET6: event_logi(VERBOSE, "Registering ULP-Callback for UDPv6 socket on port %u",ntohs(my_port)); my_address.sin6.sin6_port = htons(my_port); break; #endif default: error_log(ERROR_MINOR, "UNKNOWN ADDRESS TYPE - CHECK YOUR PROGRAM !"); break; } new_sfd = adl_open_udp_socket(&my_address); if (new_sfd != -1) { result = adl_register_fd_cb(new_sfd, POLLIN | POLLPRI, scf); event_logi(INTERNAL_EVENT_0, "Registered ULP-Callback: now %d registered callbacks !!!",result); return new_sfd; } return -1; } /** * this function is supposed to register a callback function for catching * input from the Unix STDIN file descriptor. We expect this to be useful * in test programs mainly, so it is provided here for convenience. * @param scf callback funtion that is called (when return is hit) * @return 0, or -1 if error ocurred */ int sctp_registerStdinCallback(sctp_socketCallback scf) { int result; /* 0 is the standard input ! */ result = adl_register_socket_cb(0, scf); event_logi(EXTERNAL_EVENT,"----------> Registered Stdin Callback: result=%d -------\n", result); return result; } /** * This function should only be called in exceptional cases...e.g. when we * communicate with the ULP via an additional socket */ int adl_register_socket_cb(gint sfd, sctp_socketCallback scf) { return (adl_register_fd_cb(sfd, POLLIN | POLLPRI, scf)); } /** * This function adds a callback that is to be called some time from now. It realizes * the timer (in an ordered list). * @param milliseconds action is to be started in milliseconds ms from now * @param action pointer to a function to be executed, when timer goes off * @return returns an id value, that can be used to cancel a timer * @author ajung */ unsigned int sctp_startTimer(unsigned int milliseconds, sctp_timerCallback timer_cb, void *param1, void *param2) { unsigned int result = 0; int res = 0; AlarmTimer item; struct timeval talarm; #ifdef HAVE_GETTIMEOFDAY res = gettimeofday(&talarm, NULL); #endif if (res < 0) error_log(ERROR_FATAL, "Gettimeofday returned an error in sctp_startTimer !\n"); adl_add_msecs_totime(&talarm, milliseconds); item.timer_type = 0; memcpy(&(item.action_time), &talarm, sizeof(struct timeval)); item.action = timer_cb; item.arg1 = param1; item.arg2 = param2; result = insert_item(&item); return (result); } /** * This function adds a callback that is to be called some time from now. It realizes * the timer (in an ordered list). * @param tid timer-id of timer to be removed * @return returns 0 on success, 1 if tid not in the list, -1 on error * @author ajung */ int sctp_stopTimer(unsigned int tid) { AlarmTimer tmp; return (remove_item(tid, &tmp)); } /** * Restarts a timer currently running * @param timer_id the value returned by set_timer for a certain timer * @param milliseconds action is to be started in milliseconds ms from now * @return new timer id , zero when there is an error (i.e. no timer) * @author ajung */ unsigned int sctp_restartTimer(unsigned int timer_id, unsigned int milliseconds) { unsigned int result; result = update_item(timer_id, milliseconds); event_logiii(VVERBOSE, "Restarted Timer : timer_id = %u, msecs = %u, result = %u", timer_id, milliseconds, result); return result; } /** * function to close a bound socket from our list of socket descriptors * @param sfd socket file descriptor to be closed * @return 0 on success, -1 for error, 1 if socket was not bound * @author ajung */ int adl_remove_cb(int sfd) { int result; result = close(sfd); if (result < 0) error_log(ERROR_FATAL, "Close Socket resulted in an error"); adl_remove_poll_fd(sfd); return result; }
Archive powered by MHonArc 2.6.19+.