//
// $Id: Mtrace.cc,v 1.1.1.1 2000/03/10 16:32:19 engin Exp $
//
// Author(s): Ramesh Govindan

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "util/Types.hh"
#include "util/Trail.hh"
#include "util/Handler.hh"
#include "util/Buffer.hh"
#include "sys/File.hh"
#include "sys/Pipe.hh"
#include "sys/Time.hh"
#include "sched/Timer.hh"
#include "sched/Dispatcher.hh"

#include "network/Mtrace.hh"
#include "network/Headers.hh"
#include "network/Network.hh"

// Constants
static const U8		MaxUnicastTtl = 255;
static const U8		MaxMulticastTtl = 192;

// Locals
static TraceCode	traceMtrace("network");
static TimeShort	maxWait(10, 0);
static int		mtraceSequence = 0;

static void
mtraceTimerHandler(void* ptr,
                   void*)
{
    ((Mtrace*) ptr)->handleTimer();
}

Mtrace::Mtrace(const Address* s,
               const Address* g,
               const Address* r,
               const Address* R,
               const Address* t,
               int h,
               const Handler& cb)
        : ListNode()
{
    done = cb;
    source = *s;
    group = *g;

    multicastSocket = NULL;
    if (r) {
        replyTo = *r;
        ASSERT(!IN_MULTICAST(replyTo.get()));
    } else {
        replyTo.set(MtraceMulticast);
    }

    if (R) {
        receiver = *R;
    } else {
        receiver.set((network->interfaces.head())->address.get());
    }
    if (t) {
        target = *t;
    } else {
        target.set(AllRoutersMulticast);
    }

    nhops = h;
    timer = NULL;
    network->pendingMtraces.append(this);
}

Mtrace::~Mtrace()
{
    if (timer) {
        delete timer;
    }
    if (multicastSocket) {
        delete multicastSocket;
    }
    responses.clear();
    network->pendingMtraces.remove(this);
}

void
Mtrace::send()
{
    Handler	th(mtraceTimerHandler, this);
    Handler	nh(NULL, NULL);
    Buffer*	buf;
    IP*		ip;
    IGMP*	igmp;
    IGMPTrace*	trace;
    RawSocket*	raw;
    TimeLong	at;
    Address	addr;

    raw = new RawSocket(nh, nh, RawSocketIGMP);
    buf = new Buffer(sizeof(IP) + sizeof(IGMP) + sizeof(IGMPTrace));

    ip = (IP*) buf->contents;
    ip->headerLength = sizeof(IP) >> 2;
    ip->version = 4;
    ip->typeOfService = 0;
    ip->totalLength = sizeof(IP) + sizeof(IGMP) + sizeof(IGMPTrace);
    ip->identifier = 0;
    ip->fragmentOffset = 0;
    ip->timeToLive = 0xff;
    ip->protocol = IPPROTO_IGMP;
    ip->source = (network->interfaces.head())->address.get();
    ip->destination = target.get();
    ip->hton();
    ip->checksum = 0;
    ip->checksum = htons(network->cksum((U16*) ip, sizeof(IP)));

    igmp = (IGMP*) (ip + 1);
    igmp->type = IGMPMtrace;
    igmp->code = nhops;
    igmp->group = group.get();
    igmp->hton();

    trace = (IGMPTrace*) (igmp + 1);
    trace->source = source.get();
    trace->destination = receiver.get();
    trace->responseAddress = replyTo.get();
    trace->timeToLive = 
        IN_MULTICAST(trace->destination) ? 
        	MaxMulticastTtl : MaxUnicastTtl;
    //    identifier = ++mtraceSequence;
    identifier = random() >> 8;
    trace->queryId = identifier;
    trace->hton();

    igmp->checksum = 0;
    igmp->checksum = network->cksum((U16*) igmp,
                                    sizeof(IGMP) + sizeof(IGMPTrace));

    addr.set(AllRoutersMulticast);
    raw->sendTo(buf->contents, buf->size, addr, 0);

    TRACE(traceMtrace, "mtrace sent to %s\n", group.name());

    delete raw;
    delete buf;
    
    if (timer) {
        delete timer;
    }
    at = dispatcher.systemClock;
    at = at + maxWait;
    timer = new Timer(th, at);

    return;
}

void
Mtrace::handleTimer()
{
    TRACE(traceMtrace, "mtrace failed to %s\n", group.name());

    timer = NULL;
    done.callBack((void*) this);
    return;
}

void
Mtrace::receive(const IGMPTrace *trace, int length)
{
    IGMPTraceResponse*	resp;
    // Save the response
    resp = (IGMPTraceResponse*) (trace + 1);
    for (int i = 0; i < (length / sizeof(IGMPTraceResponse)); i++) {
            Mtrace::Response*	response;
	    
            response = new Mtrace::Response(resp);
	    response->ntoh();
            responses.append(response);
            resp++;
    }
    
    done.callBack((void*) this);
    return;
}

Mtrace::Response::Response(IGMPTraceResponse* r)
	: ListNode()
{
    response = *r;
}

Mtrace::Response::~Response()
{
    // Empty
}

//  Copyright (c) 1994 by the University of Southern California.
//  All rights reserved.
//
//  Permission to use, copy, modify, and distribute this software and
//  its documentation in source and binary forms for lawful
//  non-commercial purposes and without fee is hereby granted, provided
//  that the above copyright notice appear in all copies and that both
//  the copyright notice and this permission notice appear in supporting
//  documentation, and that any documentation, advertising materials,
//  and other materials related to such distribution and use acknowledge
//  that the software was developed by the University of Southern
//  California and/or Information Sciences Institute.
//  The name of the University of Southern California may not
//  be used to endorse or promote products derived from this software
//  without specific prior written permission.
//
//  THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
//  ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE.  THIS SOFTWARE IS
//  PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
//  INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
//  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND 
//  NON-INFRINGEMENT.
//
//  IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
//  SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
//  TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
//  THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
//  Questions concerning this software should be directed to 
//  scan@isi.edu.
//


