LINUX.ORG.RU

[linux][tcp][bind] слушать порт на localhost и 0.0.0.0

 , ,


0

1

Неожиданно столкнулся с проблемой забиндиться на localhost:80 и 0.0.0.0:80. Во фре точно работало, а вот в линухе не захотело:

>>> import socket
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> s.bind(("0.0.0.0", 8080))
>>> s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> s2.bind(("127.0.0.1", 8080))
Traceback (most recent call last):
  File "<input>", line 1, in <module>
socket.error: [Errno 98] Address already in use
>>> 

От этого есть лекарство? В sysctl ничего похожего не нашёл, в сырцы ведра лезть лень :(. Если разнести по разным ip то всё работает, но я не люблю хардкодить айпишники.

Проверено на разных вёдрах, в том числе и 3.0

★★★★★

а что не так? 0.0.0.0 включает в себя 127.0.0.1, если есть такой интерфейс.

anonymous
()
Ответ на: комментарий от anonymous

а что не так? 0.0.0.0 включает в себя 127.0.0.1

Ну вот на фре это не так и поведение фряхи более гибкое(там, вроде, даже sysctl есть для тонкой настройки поведения). А тут приходится хардкодить айпишник т.к. забиндится на * не получается.

Моё мнение эта конструкция должна работать а соединение должно уходить к «максимально близкому» слушающему сокету. Ладно, полез в сырцы...

true_admin ★★★★★
() автор топика

не знаю как во Фре, а в Опене точно не работает и никогда (насколько хватает истории цвс) не работало. и не должно - это просто логически не правильно.

val-amart ★★★★★
()
Ответ на: комментарий от true_admin

По моему это и не должно работать ибо особого смысла так делать нету. На крайняк вешаешь на 0.0.0.0 и правило в iptables если dst 127.0.0.1 то редирект на этот порт.

sn1ln
()
Ответ на: комментарий от sn1ln

На крайняк вешаешь на 0.0.0.0 и правило в iptables если dst 127.0.0.1 то редирект на этот порт.

Зачем?

Deleted
()

может тебе INADDR_ANY нужен?

phoenix ★★★★
()
Ответ на: комментарий от true_admin

Не заставляй меня высказывать все что я думаю теми словами, которыми думаю. Тебе уже объяснили, что 0.0.0.0 это «все адреса», и 127.0.0.1 под это определение тоже попадает. Если тебе нужно сортировать сокеты на основании адреса, на который пришел запрос, используй привязку на 0.0.0.0 с последующей сортировкой на основании getsockname()

no-dashi ★★★★★
()
Ответ на: комментарий от true_admin

>поведение фряхи более гибкое(там, вроде, даже sysctl есть для тонкой настройки поведения)
я постоянно слышу нечто подобное.
<troll>кстати, объясните мне, как можно критиковать linux за отсутствие четкой структуры и зоопарк из ПО, когда в freebsd есть 3 пакетных фильтра (ipf, ipfw, pf) и пара вариантов nat</troll>

fr_butch
()
Ответ на: комментарий от fr_butch

кстати, объясните мне…

Наличие «3 пакетных фильтров (ipf, ipfw, pf) и пары вариантов nat» никак не связано с «отсутствием четкой структуры и зоопарком из ПО».

baverman ★★★
()
Ответ на: комментарий от baverman

а разве три файервола - не зоопарк?
и как объяснить остановку загрузки системы при ошибке в конфигурации например ipsec (setkey)? я вообще мягко скажем удивился. админг фрибсд для бывших саперов?

fr_butch
()
Ответ на: комментарий от fr_butch

а разве три файервола - не зоопарк?

Нет. Есть они не просят.

И вообще оффтоп пошел.

baverman ★★★
()

возможно вам было нужно setsockopt(s,SOL_SOCKET,SO_BINDTODEVICE,«lo»,2)? то есть привязать сокет к конкретному интерфейсу ?

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

то есть привязать сокет к конкретному интерфейсу ?

Спасибо, сейчас попробую.

true_admin ★★★★★
() автор топика
Ответ на: комментарий от baverman

А чем всё-таки не устраивает слушание только на 0.0.0.0?

Хочу на *:80 nginx, на localhost:80 apache. Это более гибка конфигурация чем вешать nginx на конкретные айпишники.

true_admin ★★★★★
() автор топика
Ответ на: комментарий от true_admin

Хочу на *:80 nginx, на localhost:80 apache.

Я бы просто сделал генерилку конфигурации (sed?), которая привязывает nginx к конкретным ip, на основе текущих поднятых интерфейсов.

baverman ★★★
()
Ответ на: комментарий от MKuznetsov

Не помогло:

>>> import socket
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> s.setsockopt(socket.SOL_SOCKET, 25, "lo")
>>> s.bind(("127.0.0.1", 8080))
>>> s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> s2.bind(("0.0.0.0", 8080))
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<string>", line 1, in bind
error: [Errno 98] Address already in use

25 это SO_BINDTODEVICE который почему-то не экспортирован в модуль socket.

В принципе, в ядре за это отвечает inet_csk_get_port. Если эта функция находит слушающий сокет то inet_bind возвращает EADDRINUSE.

true_admin ★★★★★
() автор топика
Ответ на: комментарий от baverman

Я бы просто сделал генерилку конфигурации

Костыль. В итоге проблему решил захардкодив ip. Я попутно хотел поднять дискуссию на тему почему бы не сделать по-другому. Но, вижу, дискуссия идёт в русле «фряха это ацтой» и «это не нужно».

true_admin ★★★★★
() автор топика
Ответ на: комментарий от true_admin

>Моё мнение...

А по моему мнению, линукс не обязан работать так, как этого ожидает бздевая домохозяйка.

anonymous
()
Ответ на: комментарий от true_admin

>25 это SO_BINDTODEVICE который почему-то не экспортирован в модуль socket.

Дарагой друх, объясни мне, чем прописывание IP отличается от прописывании имен интерфейсов, которые (о ужас!) могут меняться (ethX vs emX, например)?

anonymous
()
Ответ на: комментарий от true_admin

> почему бы не сделать по-другому

Сделать конечно можно, нет ничего невозможного, но в ГОСТах прописано именно такое поведение, и очень маловероятно что что-то изменится. Да и не нужно это, все уже привыкли.

redixin ★★★★
()
Ответ на: комментарий от true_admin

думаю, так понятнее будет :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define MYPORT 8197
int devsock(char *dev,int port) {
	int s;
	struct sockaddr_in addr;
	s=socket(PF_INET,SOCK_STREAM,0);
	if (s<0) {
		fprintf(stderr,"socket : %s\n",strerror(errno));
		exit(1);
	}
	if (setsockopt(s,SOL_SOCKET,SO_BINDTODEVICE,dev,strlen(dev))<0) {
		fprintf(stderr,"setsockopt : %s\n",strerror(errno));
		exit(1);
	}
	addr.sin_port=htons(port);
	addr.sin_addr.s_addr=INADDR_ANY;
	if (bind(s,(struct sockaddr *)&addr,sizeof(struct sockaddr_in))<0) {
		fprintf(stderr,"bind : %s\n",strerror(errno));
		exit(1);
	}
	return s;
}
int main() {
	int s1,s2;
	printf("Create socket 1\n");
	s1=devsock("lo",MYPORT);
	printf("Create socket 2\n");
	s2=devsock("eth0",MYPORT);
	printf("Ok\n");
	shutdown(s1,2);
	shutdown(s2,2);
	return 0;			
}
и как результат :
$ ./a.out 
Create socket 1
setsockopt : Operation not permitted
$ sudo ./a.out 
Create socket 1
Create socket 2
Ok
$

MKuznetsov ★★★★★
()
Ответ на: комментарий от redixin

но в ГОСТах прописано именно такое поведение

Где это прописано?

все уже привыкли

пофиг на всех, если бы все шли на поводу у большинства то линуха бы тоже не было.

true_admin ★★★★★
() автор топика
Ответ на: комментарий от MKuznetsov

Понял, чтобы всё работало надо чтобы все сокеты были привязаны к определённому интерфейсу.

Попробовал привязать к интерфейсу any, но не сработало, «no such device». Видимо тут его указывать нельзя.

Спасибо больше за разъяснения.

true_admin ★★★★★
() автор топика
Ответ на: комментарий от redixin

Ну линух не посикс. Потом посикс платный, я даже проверить не могу что там написано.

Я скажу какой логики я придерживаюсь. Для меня 0.0.0.0 это any-интерфейс. Соответственно не вижу проблем забиндиться.

Хотя, если рассуждать, такой фигни как два вебсервера друг за другом тоже быть не должно. Тем более на одном порту. Просто софт кривой, если повесить на другой порт то будет редиректы кривые слать. Можно это на фронтенде править, но это ещё большее усугубление ситуации.

true_admin ★★★★★
() автор топика
Ответ на: комментарий от true_admin

В man bind там нету ничего

в man bind есть отсылка на man 7 ip в котором чёрным по белому написано, что «Если при вызове bind указать INADDR_ANY, то сокет будет связан со __всеми__ локальными интерфейсами» («всеми» подчёркнуто). То есть если bind не может связать сокет со __всеми__ интерфейсами (когда один уже занят), то он честно говорит что «address already in use». Иное поведение порождает неопределённость и не должно иметь места быть :)

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

«Если при вызове bind указать INADDR_ANY, то сокет будет связан со __всеми__ локальными интерфейсами»

Это я видел :).

То есть если bind не может связать сокет со __всеми__ интерфейсами (когда один уже занят), то он честно говорит что «address already in use». Иное поведение порождает неопределённость и не должно иметь места быть :)

Это логично, я не спорю. Я просто хочу сказать что так как сделано во фре бывает удобным и совсем не мешает жить. Проверено на нескольких сотнях машин. На лоре даже и не знали о такой фиче :).

Потом есть и другая логика. ANY это интерфейс который захватывает пакеты если они не подошли другим интерфейсам. Но да, по стандарту, выходит, так быть не должно. Короче, хочу sysctl-ку которая бы регулировала это. Стандарты-стандартами, но это не значит что нельзя сделать лучше.

В общем спасибо за ответы по делу, я рад что на лоре ещё остались грамотные люди.

true_admin ★★★★★
() автор топика
Ответ на: комментарий от true_admin

просто к слову, благо что самому стало любопытно.

man 7 bind адресует за деталями

EINVAL Сокет уже привязан к какому-то адресу. Эта ошибка в будущем может не выдаваться, смотри linux/unix/sock.c, где описаны детали.

что-то я этот файл с деталями несмог за 10 минут найти ни в glibc http://sourceware.org/git/?p=glibc.git;a=tree ни в ядре http://git.kernel.org/?p=linux/kernel/git/longterm/linux-2.6.33.y.git;a=tree

то ли я старею, то ли страницу man надо править..

p.s. раньше были удобные сервисы с основными исходниками обработанными doxygen или им подобными и с комментариями - что то и их тоже нет :(

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

Это должен быть ядерный файл linux/net/core/sock.c (в glibc не должно быть такой логики), но там комментов нет. Возможно уже наступило «будущее» :)

true_admin ★★★★★
() автор топика
Ответ на: комментарий от true_admin

> Для меня 0.0.0.0 это any-интерфейс. Соответственно не вижу проблем забиндиться.

Что за бред? Какой интерфейс? Об интерфейсах речи вообще не идет, речь об адресах и портах. 0.0.0.0 это INADDR_ANY. Нужно переводить слово ANY, или сам знаешь?

127.0.0.1 тоже попадает под ANY, и если ты слушаешь 80 на ANY адресе, то ты слушаешь 80 и на 127.0.0.1 тоже. Что не ясно?

Если ты уже забиндил 80 на 127.0.0.1 то ты уже не можешь забиндить 80 на any, потому что это уже будет не any, а any_кроме_127.0.0.1 а это уже не any.

redixin ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.