Параметры оповещения маршрутизатора для пакетов IGMPv2
Я пытаюсь подделать пакет запроса членства IGMPv2 и отправить его через сокет RAW.
RFC 3376 гласит:
Сообщения IGMP инкапсулированы в дейтаграммы IPv4 с номером протокола IP 2. Каждое сообщение IGMP, описанное в этом документе, отправляется с IP-адресом времени жизни 1, приоритетом IP-адреса межсетевого управления (например, тип услуги 0xc0), и содержит опцию IP Router Alert [RFC-2113] в своем заголовке IP
Таким образом, флаг IP_ROUTER_ALERT должен быть установлен.
Я пытаюсь подделать строго необходимый пакет (например, только заголовок IGMP и полезную нагрузку), поэтому я использую setsockopt для редактирования параметров IP.
некоторые полезные переменные:
#define C_IP_MULTICAST_TTL 1
#define C_IP_ROUTER_ALERT 1
int sockfd = 0;
int ecsockopt = 0;
int bytes_num = 0;
int ip_multicast_ttl = C_IP_MULTICAST_TTL;
int ip_router_alert = C_IP_ROUTER_ALERT;
Вот как я открываю разъем RAW:
sock_domain = AF_INET;
sock_type = SOCK_RAW;
sock_proto = IPPROTO_IGMP;
if ((ecsockopt = socket(sock_domain,sock_type,sock_proto)) < 0) {
printf("Error %d: Can't open socket.\n", errno);
return 1;
} else {
printf("** Socket opened.\n");
}
sockfd = ecsockopt;
Затем я установил опцию TTL и Router Alert:
// Set the sent packets TTL
if((ecsockopt = setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL, &ip_multicast_ttl, sizeof(ip_multicast_ttl))) < 0) {
printf("Error %d: Can't set TTL.\n", ecsockopt);
return 1;
} else {
printf("** TTL set.\n");
}
// Set the Router Alert
if((ecsockopt = setsockopt(sockfd, IPPROTO_IP, IP_ROUTER_ALERT, &ip_router_alert, sizeof(ip_router_alert))) < 0) {
printf("Error %d: Can't set Router Alert.\n", ecsockopt);
return 1;
} else {
printf("** Router Alert set.\n");
}
Setsockopt IP_ROUTER_ALERT возвращает 0. После подделки пакета я отправляю его с sendto следующим образом:
// Send the packet
if((bytes_num = sendto(sockfd, packet, packet_size, 0, (struct sockaddr*) &mgroup1_addr, sizeof(mgroup1_addr))) < 0) {
printf("Error %d: Can't send Membership report message.\n", bytes_num);
return 1;
} else {
printf("** Membership report message sent. (bytes=%d)\n",bytes_num);
}
Пакет отправлен, но опция IP_ROUTER_ALERT (проверено с помощью wireshark) отсутствует. Я делаю что-то неправильно? Есть ли другие методы для установки параметра IP_ROUTER_ALERT?
Заранее спасибо.
4 ответа
Наконец я узнал, что IP_ROUTER_ALERT
должен быть установлен ядром Linux. IGMP запросы на членство отправляются после IP_ADD_MEMBERSHIP
сделано, и ядро отвечает за настройку IP_ROUTER_ALERT
флаг.
Я не знаю, почему ваш код не работает (мне кажется, это нормально), но я могу предложить обходной путь: добавьте еще один слой в ваш необработанный сокет и создайте кадры Ethernet. Возможно, вы также захотите взглянуть на Libnet, который обрабатывает такие пакеты для вас.
В документации говорится:
Передайте все пересылаемые пакеты с опцией IP Router Alert, установленной для этого сокета. Действительно только для необработанных сокетов. Это полезно, например, для пользовательских демонов RSVP. Отбитые пакеты не передаются ядром, ответственность за их повторную отправку лежит на пользователях. Привязка сокетов игнорируется, такие пакеты фильтруются только по протоколу. Ожидается целочисленный флаг.
Это звучит так, как будто опция имеет значение только при получении пакетов в сокете, а не при их отправке. Если вы отправляете необработанные пакеты, не могли бы вы сами установить необходимую опцию в заголовке IP?
В качестве ссылки я бы порекомендовал одну из многих программ, поддерживающих IGMP. Одним из примеров является igmpproxy
: https://github.com/ViToni/igmpproxy/blob/logging/src/igmp.c#L54
/*
* Open and initialize the igmp socket, and fill in the non-changing
* IP header fields in the output packet buffer.
*/
void initIgmp(void) {
struct ip *ip;
recv_buf = malloc(RECV_BUF_SIZE);
send_buf = malloc(RECV_BUF_SIZE);
k_hdr_include(true); /* include IP header when sending */
k_set_rcvbuf(256*1024,48*1024); /* lots of input buffering */
k_set_ttl(1); /* restrict multicasts to one hop */
k_set_loop(false); /* disable multicast loopback */
ip = (struct ip *)send_buf;
memset(ip, 0, sizeof(struct ip));
/*
* Fields zeroed that aren't filled in later:
* - IP ID (let the kernel fill it in)
* - Offset (we don't send fragments)
* - Checksum (let the kernel fill it in)
*/
ip->ip_v = IPVERSION;
ip->ip_hl = (sizeof(struct ip) + 4) >> 2; /* +4 for Router Alert option */
ip->ip_tos = 0xc0; /* Internet Control */
ip->ip_ttl = MAXTTL; /* applies to unicasts only */
ip->ip_p = IPPROTO_IGMP;
allhosts_group = htonl(INADDR_ALLHOSTS_GROUP);
allrouters_group = htonl(INADDR_ALLRTRS_GROUP);
alligmp3_group = htonl(INADDR_ALLIGMPV3_GROUP);
}
и https://github.com/ViToni/igmpproxy/blob/logging/src/igmp.c#L271
/*
* Construct an IGMP message in the output packet buffer. The caller may
* have already placed data in that buffer, of length 'datalen'.
*/
static void buildIgmp(uint32_t src, uint32_t dst, int type, int code, uint32_t group, int datalen) {
struct ip *ip;
struct igmp *igmp;
extern int curttl;
ip = (struct ip *)send_buf;
ip->ip_src.s_addr = src;
ip->ip_dst.s_addr = dst;
ip_set_len(ip, IP_HEADER_RAOPT_LEN + IGMP_MINLEN + datalen);
if (IN_MULTICAST(ntohl(dst))) {
ip->ip_ttl = curttl;
} else {
ip->ip_ttl = MAXTTL;
}
/* Add Router Alert option */
((unsigned char*)send_buf+MIN_IP_HEADER_LEN)[0] = IPOPT_RA;
((unsigned char*)send_buf+MIN_IP_HEADER_LEN)[1] = 0x04;
((unsigned char*)send_buf+MIN_IP_HEADER_LEN)[2] = 0x00;
((unsigned char*)send_buf+MIN_IP_HEADER_LEN)[3] = 0x00;
igmp = (struct igmp *)(send_buf + IP_HEADER_RAOPT_LEN);
igmp->igmp_type = type;
igmp->igmp_code = code;
igmp->igmp_group.s_addr = group;
igmp->igmp_cksum = 0;
igmp->igmp_cksum = inetChksum((unsigned short *)igmp,
IP_HEADER_RAOPT_LEN + datalen);
}