Profil von Thomas Boor auf LinkedIn anzeigen Instagram

Thomas Boor


UNIX-Systemprogrammierung


[Home]   [Impressum]   [Kontakt]   [CV (curriculum vitae) ]   [Veröffentlichungen]   [Vermittler für IT-Freelancer]
[IMS Infos]   [IT Infos]   [Internet-Tips]   [Info-Pages & Service-URLs]   [Downloads]   [Internet Radio]   [Aphorismen & Zitate]
[(my) blog]

Setzen der QoS-per-packet-Werte bei IPv4 und IPv6

Mögliche Werte des Qos-Values bei IPv4 (IP_TOS) und IPv6 (IPV6_TCLASS:

Setzen des dscp-Values als dauerhafter Wert eines sockets:

    void set_dscp(int fd, unsigned dscp, sa_family_t af)
    {
        assert(fd >= 0 && "ioops::set_dscp called with invalid fd");
        int rc(0);
        switch (af) {
            case AF_INET6:
                rc = mysetsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &dscp, sizeof dscp);
                break;
            case AF_INET:
                rc = mysetsockopt(fd, IPPROTO_IP, IP_TOS, &dscp, sizeof dscp);
                break;
            default:
                assert(!"set_dscp unknown address family");
                break;
        }
        (void)rc; // to avoid compiler warning if assert is not compiled
        assert(rc == 0 && "setting of dscp value failed");
    }

Die Bestimmung der sa_family_t ist in den meisten Fällen eine Abfrage der 'sa_family' der gegebenen Adresse. Allerdings kann in einer IPv6-Adresse eine IPv4-Adresse gemapped sein - dies muss ermittelt werden. Hat eine IPv6-Adresse in den ersten 80 Bits '0', dann 16-mal '1', handlet es ich um eine gemappte IPv4-Adresse:

    sa_family_t detect_sa_family(const sockaddr& sa) {
        sa_family_t of(sa.sa_family);
        assert((of == AF_INET || of == AF_INET6)
               && "ioops::detect_sa_family called for invalid adress family");
        sa_family_t af(of);
        if (of == AF_INET6) {
            struct sockaddr_in6* s6 = (struct sockaddr_in6*) sa.sa_data;
            struct in6_addr* in6a = &s6->sin6_addr;
            const uint32_t* data = (const uint32_t*) in6a->s6_addr;
            if ((data[0] == 0) && (data[1] == 0) && ((data[2] & htonl(0xffff0000)) == 0xffff))
                af = AF_INET;
            else
                af = AF_INET6;
        }
        return af;
    }

Möchte man 'Pro-Paket-Qos' einsetzen, hilft das dauerhafte Setzen der setsockopt nicht. Hier muss man mit sog. 'ancillary'-Data arbeiten; pro sendmsg wird im verwendeten 'msghdr' der Qos-Value als ancillary-data in einer cmsghdr-Struktur hinzugefügt: (Die angeommene Klasse hat dabei den verwendeten-Socketdescriptor 'fd_' als Member)

    int
    Class::sendmsg(struct msghdr* mh, const sockaddr& to, unsigned dscp, int flags)
    {
        char buf[CMSG_SPACE(sizeof(unsigned))];
        mh->msg_control = (caddr_t) buf;
        mh->msg_controllen = CMSG_LEN(sizeof(unsigned));
        sa_family_t af(detect_sa_family(to));
        switch (af) {
            case AF_INET6:
                cmsg->cmsg_level = IPPROTO_IPV6;
                cmsg->cmsg_type  = IPV6_TCLASS;
                break;
            case AF_INET:
                cmsg->cmsg_level = IPPROTO_IP;
                cmsg->cmsg_type  = IP_TOS;
                break;
            default:
                assert(!"fill dscp cmsg called for invalid address family");
                break;
        }
        cmsg->cmsg_len  = CMSG_LEN(sizeof(unsigned));
        *(unsigned*)(CMSG_DATA(cmsg)) = dscp;
        return ::sendmsg(fd_, mh, flags);
    }

Möchte man auf Empfänger-Seite ermitteln, ob der Sender ein Qos-Setting als 'ancillary'-data mitgegeben hat, kann er dies per setsockopt bei seinem kernel beauftragen.

    void order_cmsg(int fd, const sockaddr& a)
    {
        sa_family_t af(detect_sa_family(a));
        int set = 1;
        switch (af) {
            case AF_INET6:
                if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVTCLASS, &set, sizeof(set)) < 0) {
                    printf("IPV6_RECVTCLASS setting failed\n");
                }
                break;
            case AF_INET:
                if (setsockopt(fd, IPPROTO_IP, IP_RECVTOS, &set, sizeof(set)) < 0) {
                    printf("IP_RECVTOS setting failed\n");
                }
                break;
            default:
                break;
        }
    }

Hat man nun per 'recvmsg' ein Paket empfangen (und zuvor die ancillary-QoS-Settings beauftragt), hängen an dem beim Empfang gefüllten 'msghdr' auch auch noch 'cmsghdr', die durchlaufen und gestest werden können. Die folgende Funktion macht dies und liefert - sofern gesetzt - den QoS-Value des Senders:

    unsigned retrieve_dscp_value(struct msghdr& m)
    {
        unsigned rc = 0;
        struct cmsghdr* cmsg = CMSG_FIRSTHDR(&m);
        while (cmsg != NULL) {
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type  == IP_TOS) {
                rc = *(xtd::punned_cast(CMSG_DATA(cmsg)));
                break;
            }
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type  == IPV6_TCLASS) {
                rc = *(xtd::punned_cast(CMSG_DATA(cmsg)));
                break;
            }
            cmsg = CMSG_NXTHDR(&m, cmsg);
        }
        return rc;
    }


Sitemap
Home  |  Kontakt  |  CV (curriculum vitae)  |  Veröffentlichungen  |  IMS Infos  |  IT Infos  |  Info-Pages & Service-URLs DiffServ / QoS in IPv4 und IPv6
Internet Radio  |  Vermittler für IT-Freelancer  |  musical essentials (en)  |  youtube links  |  Aphorismen und Zitate  |  Latein für Smalltalker  |  Downloads  |  Impressum
[fun] [Lyrik] [MatheGimmicks] [Bauernregeln] [Witze]
CV in deutsch CV in english