So binden Sie einen Raw-Socket an eine bestimmte Schnittstelle

Lesezeit: 5 Minuten

Benutzeravatar von Dima
Dima

Meine Anwendung läuft auf CentOS 5.5. Ich verwende Raw-Socket, um Daten zu senden:

sd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if (sd < 0) {
  // Error
}
const int opt_on = 1;
rc = setsockopt(m_SocketDescriptor, IPPROTO_IP, IP_HDRINCL, &opt_on, sizeof(opt_on));
if (rc < 0) {
  close(sd);
  // Error
}
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = my_ip_address;

if (sendto(m_SocketDescriptor, DataBuffer, (size_t)TotalSize, 0, (struct sockaddr *)&sin, sizeof(struct sockaddr)) < 0)  {
  close(sd);
  // Error
}

Wie kann ich diesen Socket an eine bestimmte Netzwerkschnittstelle (z. B. eth1) binden?

  • Warum würden Sie das tun wollen? Ihr Programm verliert die Portabilität, es sei denn, Sie sind sicher, dass Ihre Maschinen Schnittstellen mit den vordefinierten Namen haben werden.

    – Andreas Schlitten

    22. Oktober 2010 um 16:52 Uhr


  • Es ist für eingebettete Geräte, die Portabilität ist nicht erforderlich. Ich habe 6 Ethernet-Ports und muss Daten über eine bestimmte Schnittstelle senden

    – Dima

    22. Oktober 2010 um 17:47 Uhr


Benutzeravatar von Andrew Sledge
Andreas Schlitten

const char *opt;
opt = "eth0";
const len = strnlen(opt, IFNAMSIZ);
if (len == IFNAMSIZ) {
    fprintf(stderr, "Too long iface name");
    return 1;
}
setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, opt, len);

Erste Zeile: Richten Sie Ihre Variable ein

Zweite Zeile: Teilen Sie dem Programm mit, an welche Schnittstelle es sich binden soll

Zeilen 3-5: Ermitteln Sie die Länge des Schnittstellennamens und prüfen Sie, ob die Größe nicht zu groß ist.

Sechszeilig: Stellen Sie die Socket-Optionen für Socket ein sdBindung an das Gerät opt.

Setsockopt-Prototyp:

int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen);

Stellen Sie außerdem sicher, dass Sie die enthalten if.h, socket.h und string.h Header-Dateien

  • Danke, es funktioniert, aber mit einer kleinen Änderung: ifreq Interface; memset(&Interface, 0, sizeof(Interface)); strncpy(Interface.ifr_ifrn.ifrn_name, “eth1”, IFNAMSIZ); if (setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, &Interface, sizeof(Interface)) < 0) { close(sd); // Fehler }

    – Dima

    22. Oktober 2010 um 20:14 Uhr


  • SO_BINDTODEVICE funktioniert nur, wenn Sie es als root ausführen, richtig? (zumindest unter Linux)

    – 332. September

    27. November 2012 um 21:29 Uhr

  • Das erste Element von “struct ifreq” ist das Zeichen[] Name der Schnittstelle. Außerdem geben Sie noch IFNAMSIZ an. Dies sollte also identisch sein (so wie das Anhängen eines zufälligen Puffers an “eth0” funktionieren sollte)

    – gsk

    23. Mai 2016 um 20:56 Uhr

  • Laut dieser anderen SO-Antwort und meiner Erfahrung mit CentOS 7 funktioniert dies nicht für einen Raw-Socket. Stattdessen müssen Sie verwenden bind()nicht der setsockopt() hier gezeigte Vorgehensweise.

    – Jason R

    8. Februar 2019 um 16:20 Uhr

  • @JasonR: Ihr Kommentar hat mir und meinem Kollegen nur kurze Stunden der Frustration gekürzt. (Wir wechseln endlich von CentOS 6 auf 7 und haben uns die Köpfe darüber zerbrochen, warum irgendein Legacy-Code nicht funktioniert.) Vielen Dank!

    – Nemo

    1. März 2019 um 18:09 Uhr

Wie bereits erwähnt, ist es richtig, die zu verwenden struct ifreq um den Schnittstellennamen anzugeben. Hier ist mein Codebeispiel.

#define SERVERPORT 5555
...
struct ifreq ifr;


/* Create the socket */
sd = socket(AF_INET, SOCK_STREAM, 0);
if (sd < 0) 
{
    printf("Error in socket() creation - %s", strerror(errno));
}

/* Bind to eth1 interface only - this is a private VLAN */
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth1");
if ((rc = setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr))) < 0)
{
    perror("Server-setsockopt() error for SO_BINDTODEVICE");
    printf("%s\n", strerror(errno));
    close(sd);
    exit(-1);
}

/* bind to an address */
memset(&serveraddr, 0x00, sizeof(struct sockaddr_in));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(SERVERPORT);
serveraddr.sin_addr.s_addr = inet_addr("9.1.2.3");

int rc = bind(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));

Ich möchte auch hinzufügen, dass es aus Sicherheitsgründen zwar gut ist, den Socket an eine Schnittstelle zu binden, die Verwendung jedoch keinen Sinn macht INADDR_ANY als lauschende IP-Adresse. Dadurch würde der Port in netstat auf allen Netzwerkschnittstellen als offen erscheinen.

Proto Recv-Q Send-Q Local Address    Foreign Address    State     User Inode      PID/Program name
tcp   0      0      0.0.0.0:5555     0.0.0.0:*          LISTEN    0    210898     26996/myserver  

Stattdessen habe ich eine spezifische IP-Adresse für die verwendete Schnittstelle angegeben (ein privates VLAN). Dies hat auch die netstat-Ausgabe behoben:

Proto Recv-Q Send-Q Local Address    Foreign Address    State     User Inode      PID/Program name
tcp   0      0      9.1.2.3:5555     0.0.0.0:*          LISTEN    0    210898     26996/myserver  

  • Wo ist angegeben, dass Sie verwenden struct ifreq ? Meine Manpage sagt: “Die übergebene Option ist eine nullterminierte Schnittstellennamenzeichenfolge mit variabler Länge.”

    – Bryan

    18. Februar 2016 um 14:15 Uhr

  • Das von Ihnen vorgeschlagene Problem (INADDR_ANY für SOCK_STREAM-Sockets) tritt beim ursprünglichen Poster, das SOCK_RAW verwendet hat, nicht auf.

    – Jon Watte

    1. Mai 2016 um 17:49 Uhr

  • @Bryan Beide Methoden sollten zumindest unter Linux funktionieren. Laut netdevice(7) ist die ifr_name string ist das erste Mitglied von struct ifreqund daher ein Zeiger auf struct ifreq oder char gleich funktionieren.

    – Will Eccles

    18. Dezember 2020 um 16:22 Uhr

Binden Sie Socket an eine bestimmte Schnittstellen-IP-Adresse

int bind_using_iface_ip(int fd, char *ipaddr, uint16_t port)
{
    struct sockaddr_in localaddr = {0};
    localaddr.sin_family    = AF_INET;
    localaddr.sin_port  = htons(port);
    localaddr.sin_addr.s_addr = inet_addr(ipaddr);
    return bind(fd, (struct sockaddr*) &localaddr, sizeof(struct sockaddr_in));
}

Binden Sie Socket an einen bestimmten Schnittstellennamen

int bind_using_iface_name(int fd, char *iface_name)
{
    return setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, iface_name, strlen(iface_name))
}

Im bind_using_iface_ipum an einen beliebigen Port zu binden 0 sollte bestanden werden. Und auch wenn die fd Ist ein Raw-Socket, muss der Port als übergeben werden 0. Dieser Bindungsmechanismus ist für alle Arten von Sockets wie Raw, Dgram und Stream üblich.

  • Dies ist die richtige Antwort. Wir sollten an die IP-Adresse gebunden sein. Nicht zu einer Schnittstelle?

    – abhiarora

    25. Februar 2020 um 8:19 Uhr

  • aber unterschiedliche Schnittstellen können dieselbe IP haben

    – Mehmet Fide

    24. Juni 2021 um 18:31 Uhr

1387490cookie-checkSo binden Sie einen Raw-Socket an eine bestimmte Schnittstelle

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy