Столкнулся с интересной задачей. Есть TCP пакет, необходимо определить от кого пришел данный пакет, от сервера или клиента в клиент-серверном TCP соединении. То есть, надо понять какой порт, dst_port или src_port, в TCP пакете принадлежит серверной стороне.
Конечно со сто процентной вероятностью нельзя сказать от кого идет пакет, от сервера или от клиента в TCP соединении, но с некоторой долей вероятности такие предположения делать можно. Сейчас реализован довольно простой алгоритм:
...
#ifndef IPPORT_RESERVED
#define IPPORT_RESERVED 1024
#endif
#ifndef IPPORT_USERRESERVED
#define IPPORT_USERRESERVED 5000
#endif
#ifndef IPPORT_HIFIRSTAUTO
#define IPPORT_HIFIRSTAUTO 49152
#endif
#ifndef IPPORT_HILASTAUTO
#define IPPORT_HILASTAUTO 65535
#endif
...
static u_char s_port_big[IPPORT_RESERVED];
#define CHECK_SRV(p, proto) (s_port_big[(p) & (IPPORT_RESERVED-1)] & (proto))
#if IPPORT_HILASTAUTO < USHRT_MAX
#define IS_USER_PORT(p) (((p) >= IPPORT_RESERVED && (p) < IPPORT_USERRESERVED) || \
((p) >= IPPORT_HIFIRSTAUTO && \
(p) <= IPPORT_HILASTAUTO))
#else
/* (p) <= IPPORT_HILASTAUTO is always true because (p) is an unsigned short */
#define IS_USER_PORT(p) (((p) >= IPPORT_RESERVED && (p) < IPPORT_USERRESERVED) || \
((p) >= IPPORT_HIFIRSTAUTO))
#endif
...
setservent(1);
while ((sv = getservent())) {
NTOHS(sv->s_port);
if (sv->s_port >= IPPORT_RESERVED) {
i = sv->s_port & (IPPORT_RESERVED-1);
if (strcmp(sv->s_proto, "tcp") == 0)
s_port_big[i] |= IPPROTO_TCP;
else if (strcmp(sv->s_proto, "udp") == 0)
s_port_big[i] |= IPPROTO_UDP;
}
}
endservent();
...
t.p_port = min(tp->th_sport, tp->th_dport);
if (t.p_port < IPPORT_RESERVED ||
(IS_USER_PORT(tp->th_sport) && IS_USER_PORT(tp->th_dport))) {
t.who_srv = tp->th_sport < tp->th_dport ? 1 : 2;
}
else if (tp->th_sport == tp->th_dport) {
t.who_srv = 3;
}
/* tp->th_sport > IPPORT_RESERVED && tp->th_dport > IPPORT_RESERVED */
else {
if (CHECK_SRV(tp->th_sport, IPPROTO_TCP)) {
t.who_srv = 1;
}
else if (CHECK_SRV(tp->th_dport, IPPROTO_TCP)) {
t.who_srv = 2;
}
else {
t.who_srv = !IS_USER_PORT(tp->th_sport) ? 1 : 2 ;
}
t.p_port = (t.who_srv == 2 ? tp->th_dport : tp->th_sport);
}
...
Если описать словами в двух словах то работает это так:
1. Заполняется массив
s_port_big значениями соответствующими истине в соответствии с описанием зарегистрированных в IANA (/etc/services) службами. Далее этот массив используется в спорных случаях для проверки того, что порт принадлежит одной из зарегистрированных в IANA службе (серверу).
Тут есть одна бага, массив определяется для 1024 значений (s_port_big[IPPORT_RESERVED]) но данные в него заносятся для 65535 вариантов. Хотя и не происходит выход за пределы массива, но он начинает содержать в некоторых случаях истинные значения для не зарегистрированных служб.
2. Если один из минимальных портов принадлежит диапазону меньшему чем 1024, то считается что этот порт принадлежит серверу.
3. Если оба порта находятся в диапазоне большем или равном чем 1024 и меньшем чем 5000 или большем или равном 49152 (так называемый
диапазон клиентских портов) то выбирается самый меньший порт и считается что он принадлежит серверу.
4. Если оба порта одинаковы, то тут нельзя сказать от кого идет пакет, от сервера или клиента.
5. Проверяется что
src_port принадлежит одной из служб в
s_port_big, если это так то это сервер.
6. Проверяется что
dst_port принадлежит одной из служб в
s_port_big, если это так то это сервер.
7. И последнее, проверяется что
src_port не принадлежит
диапазону клиентских портов, если это так то это порт сервера, иначе
dst_port порт сервера.
Конечно это не самый идеальный алгоритм. Поэтому я и советуюсь, может есть более лучший алгоритм, который будет работать с более высокой вероятностью определения порта сервера?.