/*
**  igmpproxy - IGMP proxy based multicast router
**  Copyright (C) 2005 Johnny Egeland <johnny@rlo.org>
**
**  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
**
**----------------------------------------------------------------------------
**
**  This software is derived work from the following software. The original
**  source code has been modified from it's original state by the author
**  of igmpproxy.
**
**  smcroute 0.92 - Copyright (C) 2001 Carsten Schill <carsten@cschill.de>
**  - Licensed under the GNU General Public License, either version 2 or
**    any later version.
**
**  mrouted 3.9-beta3 - Copyright (C) 2002 by The Board of Trustees of
**  Leland Stanford Junior University.
**  - Licensed under the 3-clause BSD license, see Stanford.txt file.
**
*/

#include "igmpproxy.h"

/*
 * Exported variables.
 */
char s1[19];        /* buffers to hold the string representations  */
char s2[19];        /* of IP addresses, to be passed to inet_fmt() */
char s3[19];        /* or inet_fmts().                             */
char s4[19];

/*
** Formats 'InAdr' into a dotted decimal string.
**
** returns: - pointer to 'St'
**
*/
char *fmtInAdr( char *St, struct in_addr InAdr ) {
    sprintf( St, "%u.%u.%u.%u",
             ((uint8_t *)&InAdr.s_addr)[ 0 ],
             ((uint8_t *)&InAdr.s_addr)[ 1 ],
             ((uint8_t *)&InAdr.s_addr)[ 2 ],
             ((uint8_t *)&InAdr.s_addr)[ 3 ] );

    return St;
}

/*
 * Convert an IP address in u_long (network) format into a printable string.
 */
char *inetFmt(uint32_t addr, char *s) {
    register unsigned char *a;

    a = (unsigned char *)&addr;
    sprintf(s, "%u.%u.%u.%u", a[0], a[1], a[2], a[3]);
    return(s);
}

/*
 * Convert an IP subnet number in u_long (network) format into a printable
 * string including the netmask as a number of bits.
 */
char *inetFmts(uint32_t addr, uint32_t mask, char *s) {
    register unsigned char *a, *m;
    int bits;

    if ((addr == 0) && (mask == 0)) {
        sprintf(s, "default");
        return(s);
    }
    a = (unsigned char *)&addr;
    m = (unsigned char *)&mask;
    bits = 33 - ffs(ntohl(mask));

    if (m[3] != 0) sprintf(s, "%u.%u.%u.%u/%d", a[0], a[1], a[2], a[3],
                           bits);
    else if (m[2] != 0) sprintf(s, "%u.%u.%u/%d",    a[0], a[1], a[2], bits);
    else if (m[1] != 0) sprintf(s, "%u.%u/%d",       a[0], a[1], bits);
    else                sprintf(s, "%u/%d",          a[0], bits);

    return(s);
}

/*
 * inet_cksum extracted from:
 *      P I N G . C
 *
 * Author -
 *      Mike Muuss
 *      U. S. Army Ballistic Research Laboratory
 *      December, 1983
 * Modified at Uc Berkeley
 *
 * (ping.c) Status -
 *      Public Domain.  Distribution Unlimited.
 *
 *      I N _ C K S U M
 *
 * Checksum routine for Internet Protocol family headers (C Version)
 *
 */
uint16_t inetChksum(uint16_t *addr, int len) {
    register int nleft = len;
    register uint16_t *w = addr;
    uint16_t answer = 0;
    register int32_t sum = 0;

    /*
     *  Our algorithm is simple, using a 32 bit accumulator (sum),
     *  we add sequential 16 bit words to it, and at the end, fold
     *  back all the carry bits from the top 16 bits into the lower
     *  16 bits.
     */
    while (nleft > 1) {
        sum += *w++;
        nleft -= 2;
    }

    /* mop up an odd byte, if necessary */
    if (nleft == 1) {
        *(uint8_t *) (&answer) = *(uint8_t *)w ;
        sum += answer;
    }

    /*
     * add back carry outs from top 16 bits to low 16 bits
     */
    sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
    sum += (sum >> 16);                 /* add carry */
    answer = ~sum;                      /* truncate to 16 bits */
    return(answer);
}
