SCTP Discussions

Text archives Help


Re: [SCTP-Discussion] API 1.0


Chronological Thread 
  • From: Andreas Jungmaier <ajung AT exp-math.uni-essen.de>
  • To: discussion AT sctp.de, Held Martin ICM N MC MI E 6 <Martin.Held AT icn.siemens.de>
  • Subject: Re: [SCTP-Discussion] API 1.0
  • Date: Tue Mar 6 08:55:01 2001
  • List-id: SCTP Discussions <discussion.sctp.de>
  • Organization: University of Essen, Germany

Hallo Martin,

> What do you mean by "numerical format" of the source address. Is this an
> ASCII string like "139.24.48.62" terminated by "\0"

Yes, exactly. That is what will be returned by the function
adl_sockunion2str(union sockunion *su, guchar * buf, size_t len)
in adaptation.c, and is essentially a wrapper to the
inet_ntoa() function for IPv4, and inet_ntop() for IPv6.

> how would an IPv6 address look like?
fe80::250:4ff:feee:c783 or 3ffe:400:f0:96:250:4ff:feee:c783
or even something like 3ffe:400:f0:96:250:4ff:132.252.150.123
(as per RFC 1884)
>
> I'm assuming, the buffer that holds the message is handled completely by
> SCTP, i.e. no "free" is required by the user, right?

Yes, it is a temporary buffer that may not exist (and probably does not)
after the callback returns. The respective code can be found in the
current adaptation.c, which I have attached to this mail.

Regards,
--

Andreas Jungmaier

----------------------------------------------------------------------------
void hamlet()
{#define question=((bb)||(!bb))}
----------------------------------------------------------------------------
Andreas Jungmaier, Dipl.-Ing.
University of Essen email:
ajung AT exp-math.uni-essen.de
Computer Networking Technology Group Tel. : +49 (0201) 183-7636
----------------------------------------------------------------------------
PGP Public Key and Fingerprint: see http://www.exp-math.uni-essen.de/~ajung
----------------------------------------------------------------------------
/*
 *  $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;
}


  • [SCTP-Discussion] API 1.0, Held Martin ICM N MC MI E 6, 03/05/2001
    • Re: [SCTP-Discussion] API 1.0, Andreas Jungmaier, 03/05/2001

Archive powered by MHonArc 2.6.19+.

Top of page