summaryrefslogtreecommitdiffstats
path: root/daemon/icmp.c
blob: 7252b7c8e06b031aaac034eb37d2fff93a426159 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>

#include <linux/ip.h>
#include <linux/icmp.h>
#include <linux/tcp.h>

#include "icmp.h"

#define BUFSIZE (10*1024)

static int icmp_socket = -1;

guint16 ipsum(guint8 *p, guint l) {
    guint32 sum = 0;
    guint i;
    
    for (i = 0; i < l-1; i += 2) 
        sum += (p[i] << 8) + p[i+1];

    if (i < l)
        sum += p[i] << 8;
    
    sum = (sum & 0xFFFF) + (sum >> 16);
    sum = (sum & 0xFFFF) + (sum >> 16);

    return ~sum;
}


int reply_icmp_error(ipq_packet_msg_t *m, int code) {
    static guint8 buf[BUFSIZE];
    struct iphdr* ip = (struct iphdr*) m->payload;
    struct icmphdr *icmp = (struct icmphdr*) buf;
    guint l, tl;
    struct sockaddr_in sa;

    icmp->type = 3;
    icmp->code = code;
    icmp->checksum = 0;
    icmp->un.frag.__unused = 0;
    icmp->un.frag.mtu = 0;

    l = ip->ihl*4 + 8;
    memcpy(&buf[sizeof(struct icmphdr)], m->payload, l);

    tl = l + sizeof(struct icmphdr);
    icmp->checksum = htons(ipsum((guint8*) icmp, tl));

    sa.sin_family = AF_INET;
    sa.sin_port = 0;
    sa.sin_addr.s_addr = ip->saddr;


    if (sendto(icmp_socket, icmp, tl, 0, (struct sockaddr*) &sa, sizeof(sa)) < 0) {
        perror("sendto()");
        return -1;
    }

    return 0;
}

int icmp_init() {
    int enable = 1;
    
    if ((icmp_socket = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
        perror("socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)");
        return -1;
        
    }
    
    if (setsockopt(icmp_socket, SOL_SOCKET, SO_BROADCAST, (char *)&enable, sizeof(enable)) < 0) {
        perror("setsockopt(.., SOL_SOCKET, SO_BROADCAST)");
        return -1;
    }

    return 0;
}


void icmp_done() {
    if (icmp_socket >= 0)
        close(icmp_socket);

    icmp_socket = -1;
}