#include "findmtu.h"

/**
 * Set up ICMPv6 error reception
 * Input: sending socket fd
 */
int recv_init(int sendfd) {
    int on;

    on = IPV6_PMTUDISC_DO;
    if (setsockopt(sendfd, SOL_IPV6, IPV6_MTU_DISCOVER, &on, sizeof(on))) {
        perror("sockopt IPV6_MTU_DISCOVER");
        exit(1);
    }

    on = 1;
    if (setsockopt(sendfd, SOL_IPV6, IPV6_RECVERR, &on, sizeof(on))) {
        perror("sockopt IPV6_RECVERR");
        exit(1);
    }

    return sendfd;
}

/**
 * Wait for reply to a probe packet
 * Input: socket fd
 * Output: 0 for timeout, 1 if a reply arrives
 */
int wait_for_reply(int fd, struct icmpv6responsefilter *filter) {
    fd_set fds;
    struct timeval tv;
    FD_ZERO(&fds);
    FD_SET(fd, &fds);
    tv.tv_sec = 2;
    tv.tv_usec = 0;
    return select(fd+1, &fds, NULL, NULL, &tv);
}

/**
 * Process ICMPv6 messages from a socket
 * Input: socket fd
 * Output: struct mtureply with address of reporting host, MTU, and ICMP type/code
 */

struct mtureply recvmtu(int fd) {
    struct msghdr msg;
    struct iovec iov;
    struct probehdr rcvbuf;
    struct sock_extended_err *e;
    struct cmsghdr *cmsg;
    char cbuf[512];
    struct mtureply reply;

    time(&rcvbuf.tv.tv_sec);

    iov.iov_base = &rcvbuf;
    iov.iov_len = sizeof(rcvbuf);

    msg.msg_name = NULL;
    msg.msg_namelen = 0;
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;
    msg.msg_flags = 0;
    msg.msg_control = cbuf;
    msg.msg_controllen = sizeof(cbuf);

    memset(reply.addr, 0, sizeof(reply.addr));
    reply.mtu = 0;
    reply.ee_type = reply.ee_code = 0;

    if(recvmsg(fd, &msg, MSG_ERRQUEUE) < 0)
        return reply;

    for(cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg))
        if (cmsg->cmsg_level == SOL_IPV6)
            if (cmsg->cmsg_type == IPV6_RECVERR)
                e = (struct sock_extended_err *)CMSG_DATA(cmsg);

    if(e->ee_origin == SO_EE_ORIGIN_ICMP6) {
        struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)(e+1);
        inet_ntop(AF_INET6, &sin6->sin6_addr, reply.addr, sizeof(reply.addr));
    }

    switch(e->ee_errno) {
        case EMSGSIZE:
            reply.mtu = e->ee_info;
            break;
        case ECONNREFUSED: /* Target reached */
            reply.mtu = -1;
            break;
        default:           /* Other error. Set it and bail */
            reply.mtu = -1;
            reply.ee_type = e->ee_type;
            reply.ee_code = e->ee_code;
            break;
    }

    return(reply);
}

struct icmpv6responsefilter *getfilter(int fd) {
    return NULL;
}


syntax highlighted by Code2HTML, v. 0.9.1