diff --git a/documentation/content/de/books/developers-handbook/secure/_index.adoc b/documentation/content/de/books/developers-handbook/secure/_index.adoc index 6e71f358cd..ecf2f6e7ca 100644 --- a/documentation/content/de/books/developers-handbook/secure/_index.adoc +++ b/documentation/content/de/books/developers-handbook/secure/_index.adoc @@ -1,226 +1,226 @@ --- title: Kapitel 3. Sicheres Programmieren -authors: +authors: - author: Murray Stokely prev: books/developers-handbook/tools next: books/developers-handbook/l10n showBookMenu: true weight: 4 params: path: "/books/developers-handbook/secure/" --- [[secure]] = Sicheres Programmieren :doctype: book :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumoffset: 3 :partnums: :source-highlighter: rouge :experimental: :images-path: books/developers-handbook/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] [[secure-synopsis]] == Zusammenfassung Dieses Kapitel beschreibt einige Sicherheitsprobleme, die UNIX(R)-Programmierer seit Jahrzehnten quälen, und inzwischen verfügbare Werkzeuge, die Programmierern helfen, Sicherheitslücken in ihrem Quelltext zu vermeiden. [[secure-philosophy]] == Methoden des sicheren Entwurfs Sichere Anwendungen zu schreiben erfordert eine sehr skeptische und pessimistische Lebenseinstellung. Anwendungen sollten nach dem Prinzip der "geringsten Privilegien" ausgeführt werden, sodass kein Prozess mit mehr als dem absoluten Minimum an Zugriffsrechten arbeitet, die er zum Erfüllen seiner Aufgabe benötigt. Wo es möglich ist, sollte Quelltext, der bereits überprüft wurde, wiederverwendet werden, um häufige Fehler, die andere schon korrigiert haben, zu vermeiden. Eine der Stolperfallen der UNIX(R)-Umgebung ist, dass es sehr einfach ist Annahmen über die Konsistenz der Umgebung zu machen. Anwendungen sollten Nutzereingaben (in allen Formen) niemals trauen, genauso wenig wie den System-Ressourcen, der Inter-Prozess-Kommunikation oder dem zeitlichen Ablauf von Ereignissen. UNIX(R)-Prozesse arbeiten nicht synchron. Daher sind logische Operationen selten atomar. [[secure-bufferov]] == Puffer-Überläufe Puffer-Überläufe gibt es schon seit den Anfängen der Von-Neuman-Architektur crossref:bibliography[cod,1]. Sie erlangten zum ersten Mal durch den Internetwurm Morris im Jahre 1988 öffentliche Bekanntheit. Unglücklicherweise funktioniert der gleiche grundlegende Angriff noch heute. Die bei weitem häufigste Form eines Puffer-Überlauf-Angriffs basiert darauf, den Stack zu korrumpieren. Die meisten modernen Computer-Systeme verwenden einen Stack, um Argumente an Prozeduren zu übergeben und lokale Variablen zu speichern. Ein Stack ist ein last-in-first-out-Puffer (LIFO) im hohen Speicherbereich eines Prozesses. Wenn ein Programm eine Funktion aufruft wird ein neuer "Stackframe" erzeugt. Dieser besteht aus den Argumenten, die der Funktion übergeben wurden und einem variabel grossem Bereich für lokale Variablen. Der "Stack-Pointer" ist ein Register, dass die aktuelle Adresse der Stack-Spitze enthält. Da sich dieser Wert oft ändert, wenn neue Werte auf dem Stack abgelegt werden, bieten viele Implementierungen einen "Frame-Pointer", der nahe am Anfang des Stack-Frames liegt und es so leichter macht lokale Variablen relativ zum aktuellen Stackframe zu adressieren. crossref:bibliography[cod,1] Die Rücksprungadresse der Funktionen werden ebenfalls auf dem Stack gespeichert und das ist der Grund für Stack-Überlauf-Exploits. Denn ein böswilliger Nutzer kann die Rücksprungadresse der Funktion überschreiben indem er eine lokale Variable in der Funktion überlaufen lässt, wodurch es ihm möglich ist beliebigen Code auszuführen. Obwohl Stack-basierte Angriffe bei weitem die Häufigsten sind, ist es auch möglich den Stack mit einem Heap-basierten (malloc/free) Angriff zu überschreiben. Die C-Programmiersprache führt keine automatischen Bereichsüberprüfungen bei Feldern oder Zeigern durch, wie viele andere Sprachen das tun. Außerdem enthält die C-Standardbibliothek eine Handvoll sehr gefährlicher Funktionen. [.informaltable] [cols="1,1", frame="none"] |=== |`strcpy`(char *dest, const char *src) | Kann den Puffer dest überlaufen lassen |`strcat`(char *dest, const char *src) | Kann den Puffer dest überlaufen lassen |`getwd`(char *buf) | Kann den Puffer buf überlaufen lassen |`gets`(char *s) | Kann den Puffer s überlaufen lassen |`[vf]scanf`(const char *format, ...) | Kann sein Argument überlaufen lassen |`realpath`(char *path, char resolved_path[]) | Kann den Puffer path überlaufen lassen |`[v]sprintf`(char *str, const char *format, ...) | Kann den Puffer str überlaufen lassen |=== === Puffer-Überlauf Beispiel Das folgende Quellcode-Beispiel enthält einen Puffer-Überlauf, der darauf ausgelegt ist die Rücksprungadresse zu überschreiben und die Anweisung direkt nach dem Funktionsaufruf zu überspringen. (Inspiriert durch crossref:bibliography[Phrack,4]) [.programlisting] .... #include stdio.h void manipulate(char *buffer) { char newbuffer[80]; strcpy(newbuffer,buffer); } int main() { char ch,buffer[4096]; int i=0; while ((buffer[i++] = getchar()) != '\n') {}; i=1; manipulate(buffer); i=2; printf("The value of i is : %d\n",i); return 0; } .... Betrachten wir nun, wie das Speicherabbild dieses Prozesses aussehen würde, wenn wir 160 Leerzeichen in unser kleines Programm eingeben, bevor wir Enter drücken. -[XXX figure here!] +[ XXX figure here! ] Offensichtlich kann man durch böswilligere Eingaben bereits kompilierten Programmtext ausführen (wie z.B. exec(/bin/sh)). === Puffer-Überläufe vermeiden -Die direkteste Lösung, um Stack-Überläufe zu vermeiden, ist immer grössenbegrenzten Speicher und String-Copy-Funktionen zu verwenden. `strncpy` und `strncat` sind Teil der C-Standardbibliothek. Diese Funktionen akzeptieren einen Längen-Parameter. Dieser Wert sollte nicht größer sein als die Länge des Zielpuffers. Die Funktionen kopieren dann bis zu `length` Bytes von der Quelle zum Ziel. Allerdings gibt es einige Probleme. Keine der Funktionen garantiert, dass die Zeichenkette NUL-terminiert ist, wenn die Größe des Eingabepuffers so groß ist wie das Ziel. Außerdem wird der Parameter length zwischen strncpy und strncat inkonsistent definiert, weshalb Programmierer leicht bezüglich der korrekten Verwendung durcheinander kommen können. Weiterhin gibt es einen spürbaren Leistungsverlust im Vergleich zu `strcpy`, wenn eine kurze Zeichenkette in einen großen Puffer kopiert wird. Denn `strncpy` fült den Puffer bis zur angegebenen Länge mit NUL auf. +Die direkteste Lösung, um Stack-Überläufe zu vermeiden, ist immer grössenbegrenzten Speicher und String-Copy-Funktionen zu verwenden. `strncpy` und `strncat` sind Teil der C-Standardbibliothek. Diese Funktionen akzeptieren einen Längen-Parameter. Dieser Wert sollte nicht größer sein als die Länge des Zielpuffers. Die Funktionen kopieren dann bis zu `length` Bytes von der Quelle zum Ziel. Allerdings gibt es einige Probleme. Keine der Funktionen garantiert, dass die Zeichenkette NUL-terminiert ist, wenn die Größe des Eingabepuffers so groß ist wie das Ziel. Außerdem wird der Parameter length zwischen strncpy und strncat inkonsistent definiert, weshalb Programmierer leicht bezüglich der korrekten Verwendung durcheinander kommen können. Weiterhin gibt es einen spürbaren Leistungsverlust im Vergleich zu `strcpy`, wenn eine kurze Zeichenkette in einen großen Puffer kopiert wird. Denn `strncpy` fült den Puffer bis zur angegebenen Länge mit NUL auf. In OpenBSD wurde eine weitere Möglichkeit zum kopieren von Speicherbereichen implementiert, die dieses Problem umgeht. Die Funktionen `strlcpy` und `strlcat` garantieren, dass das Ziel immer NUL-terminiert wird, wenn das Argument length ungleich null ist. Für weitere Informationen über diese Funktionen lesen Sie bitte crossref:bibliography[OpenBSD,6]. Die OpenBSD-Funktionen `strlcpy` und `strlcat` sind seit Version 3.3 auch in FreeBSD verfügbar. ==== Compiler-basierte Laufzeitüberprüfung von Grenzen Unglücklicherweise gibt es immer noch sehr viel Quelltext, der allgemein verwendet wird und blind Speicher umherkopiert, ohne eine der gerade besprochenen Funktionen, die Begrenzungen unterstützen, zu verwenden. Glücklicherweise gibt es einen Weg, um solche Angriffe zu verhindern - Überprüfung der Grenzen zur Laufzeit, die in verschiedenen C/C++ Compilern eingebaut ist. ProPolice ist eine solche Compiler-Eigenschaft und ist in den man:gcc[1] Versionen 4.1 und höher integriert. Es ersetzt und erweitert die man:gcc[1] StackGuard-Erweiterung von früher. ProPolice schützt gegen stackbasierte Pufferüberläufe und andere Angriffe durch das Ablegen von Pseudo-Zufallszahlen in Schlüsselbereichen des Stacks bevor es irgendwelche Funktionen aufruft. Wenn eine Funktion beendet wird, werden diese "Kanarienvögel" überprüft und wenn festgestellt wird, dass diese verändert wurden wird das Programm sofort abgebrochen. Dadurch wird jeglicher Versuch, die Rücksprungadresse oder andere Variablen, die auf dem Stack gespeichert werden, durch die Ausführung von Schadcode zu manipulieren, nicht funktionieren, da der Angreifer auch die Pseudo-Zufallszahlen unberührt lassen müsste. Ihre Anwendungen mit ProPolice neu zu kompilieren ist eine effektive Maßnahme, um sie vor den meisten Puffer-Überlauf-Angriffen zu schützen, aber die Programme können noch immer kompromittiert werden. ==== Bibliotheks-basierte Laufzeitüberprüfung von Grenzen Compiler-basierte Mechanismen sind bei Software, die nur im Binärformat vertrieben wird, und die somit nicht neu kompiliert werden kann völlig nutzlos. Für diesen Fall gibt es einige Bibliotheken, welche die unsicheren Funktionen der C-Bibliothek (`strcpy`, `fscanf`, `getwd`, etc..) neu implementieren und sicherstellen, dass nicht hinter den Stack-Pointer geschrieben werden kann. * libsafe * libverify * libparanoia Leider haben diese Bibliotheks-basierten Verteidigungen mehrere Schwächen. Diese Bibliotheken schützen nur vor einer kleinen Gruppe von Sicherheitslücken und sie können das eigentliche Problem nicht lösen. Diese Maßnahmen können versagen, wenn die Anwendung mit -fomit-frame-pointer kompiliert wurde. Außerdem kann der Nutzer die Umgebungsvariablen LD_PRELOAD und LD_LIBRARY_PATH überschreiben oder löschen. [[secure-setuid]] == SetUID-Themen Es gibt zu jedem Prozess mindestens sechs verschiedene IDs, die diesem zugeordnet sind. Deshalb müssen Sie sehr vorsichtig mit den Zugriffsrechten sein, die Ihr Prozess zu jedem Zeitpunkt besitzt. Konkret bedeutet dass, das alle seteuid-Anwendungen ihre Privilegien abgeben sollten, sobald sie diese nicht mehr benötigen. Die reale Benutzer-ID kann nur von einem Superuser-Prozess geändert werden. Das Programm login setzt sie, wenn sich ein Benutzer am System anmeldet, und sie wird nur selten geändert. Die effektive Benutzer-ID wird von der Funktion `exec()` gesetzt, wenn ein Programm das seteuid-Bit gesetzt hat. Eine Anwendung kann `seteuid()` jederzeit aufrufen, um die effektive Benutzer-ID entweder auf die reale Benutzer-ID oder die gespeicherte set-user-ID zu setzen. Wenn eine der `exec()`-Funktionen die effektive Benutzer-ID setzt, wird der vorherige Wert als gespeicherte set-user-ID abgelegt. [[secure-chroot]] == Die Umgebung ihrer Programme einschränken Die herkömmliche Methode, um einen Prozess einzuschränken, besteht in dem Systemaufruf `chroot()`. Dieser Aufruf ändert das Wurzelverzeichnis, auf das sich alle Pfadangaben des Prozesses und jegliche Kind-Prozesse beziehen. Damit dieser Systemaufruf gelingt, muss der Prozess Ausführungsrechte (Durchsuchungsrechte) für das Verzeichnis haben, auf das er sich bezieht. Die neue Umgebung wird erst wirksam, wenn Sie mittels `chdir()` in Ihre neue Umgebung wechseln. Es sollte erwähnt werden, dass ein Prozess recht einfach aus der chroot-Umgebung ausbrechen kann, wenn er root-Rechte besitzt. Das kann man erreichen, indem man Gerätedateien anlegt, um Kernel-Speicher zu lesen, oder indem man einen Debugger mit einem Prozess außerhalb seiner man:chroot[8]-Umgebung verbindet, oder auf viele andere kreative Wege. Das Verhalten des Systemaufrufs `chroot()` kann durch die kern.chroot.allow_open_directories `sysctl`-Variable beeinflusst werden. Wenn diese auf 0 gesetzt ist, wird `chroot()` mit EPERM fehlschlagen, wenn irgendwelche Verzeichnisse geöffnet sind. Wenn die Variable auf den Standardwert 1 gesetzt ist, wird `chroot()` mit EPERM fehlschlagen, wenn irgendwelche Verzeichnisse geöffnet sind und sich der Prozess bereits in einer `chroot()`-Umgebung befindet. Bei jedem anderen Wert wird die Überprüfung auf geöffnete Verzeichnisse komplett umgangen. === Die Jail-Funktionalität in FreeBSD Das Konzept einer Jail (Gefängnis) erweitert `chroot()`, indem es die Macht des Superusers einschränkt, um einen echten 'virtuellen Server' zu erzeugen. Wenn ein solches Gefängnis einmal eingerichtet ist, muss die gesamte Netzwerkkommunikation über eine bestimmte IP-Adresse erfolgen und die "root-Privilegien" innerhalb der Jail sind sehr stark eingeschränkt. Solange Sie sich in einer Jail befinden, werden alle Tests auf Superuser-Rechte durch den Aufruf von `suser()` fehlschlagen. Allerdings wurden einige Aufrufe von `suser()` abgeändert, um die neue `suser_xxx()`-Schnittstelle zu implementieren. Diese Funktion ist dafür verantwortlich, festzustellen, ob bestimmte Superuser-Rechte einem eingesperrten Prozess zur Verfügung stehen. Ein Superuser-Prozess innerhalb einer Jail darf folgendes: * Berechtigungen verändern mittels: `setuid`, `seteuid`, `setgid`, `setegid`, `setgroups`, `setreuid`, `setregid`, `setlogin` * Ressourcenbegrenzungen setzen mittels `setrlimit` * Einige sysctl-Variablen (kern.hostname) verändern * `chroot()` * Ein Flag einer vnode setzen: `chflags`, `fchflags` * Attribute einer vnode setzen wie Dateiberechtigungen, Eigentümer, Gruppe, Größe, Zugriffszeit und Modifikationszeit * Binden eines Prozesses an einen öffentlichen privilegierten Port (ports 1024) ``Jail``s sind ein mächtiges Werkzeug, um Anwendungen in einer sicheren Umgebung auszuführen, aber sie haben auch ihre Nachteile. Derzeit wurden die IPC-Mechanismen noch nicht an `suser_xxx` angepasst, so dass Anwendungen wie MySQL nicht innerhalb einer Jail ausgeführt werden können. Der Superuser-Zugriff hat in einer Jail nur eine sehr eingeschränkte Bedeutung, aber es gibt keine Möglichkeit zu definieren was "sehr eingeschränkt" heißt. === POSIX(R).1e Prozess Capabilities POSIX(R) hat einen funktionalen Entwurf (Working Draft) herausgegeben, der Ereignisüberprüfung, Zugriffskontrolllisten, feiner einstellbare Privilegien, Informationsmarkierung und verbindliche Zugriffskontrolle enthält. Dies ist im Moment in Arbeit und das Hauptziel des http://www.trustedbsd.org/[TrustedBSD]-Projekts. Ein Teil der bisherigen Arbeit wurde in FreeBSD-CURRENT übernommen (cap_set_proc(3)). [[secure-trust]] == Vertrauen Eine Anwendung sollte niemals davon ausgehen, dass irgendetwas in der Nutzerumgebung vernünftig ist. Das beinhaltet (ist aber sicher nicht darauf beschränkt): Nutzereingaben, Signale, Umgebungsvariablen, Ressourcen, IPC, mmaps, das Arbeitsverzeichnis im Dateisystem, Dateideskriptoren, die Anzahl geöffneter Dateien, etc.. Sie sollten niemals annehmen, dass Sie jede Art von inkorrekten Eingaben abfangen können, die ein Nutzer machen kann. Stattdessen sollte Ihre Anwendung positive Filterung verwenden, um nur eine bestimmte Teilmenge an Eingaben zuzulassen, die Sie für sicher halten. Ungeeignete Datenüberprüfung ist die Ursache vieler Exploits, besonders für CGI-Skripte im Internet. Bei Dateinamen müssen Sie besonders vorsichtig sein, wenn es sich um Pfade ("../", "/"), symbolische Verknüpfungen und Shell-Escape-Sequenzen handelt. Perl bietet eine wirklich coole Funktion, den sogenannten "Taint"-Modus, der verwendet werden kann, um zu verhindern, dass Skripte Daten, die von außerhalb des Programmes stammen, auf unsichere Art und Weise verwenden. Dieser Modus überprüft Kommandozeilenargumente, Umgebungsvariablen, Lokalisierungsinformationen, die Ergebnisse von Systemaufrufen (`readdir()`, `readlink()`, `getpwxxx()`) und alle Dateieingaben. [[secure-race-conditions]] == Race-Conditions Eine Race-Condition ist ein unnormales Verhalten, das von einer unerwarteten Abhängigkeit beim Timing von Ereignissen verursacht wird. Mit anderen Worten heißt das, ein Programmierer nimmt irrtümlicher Weise an, dass ein bestimmtes Ereignis immer vor einem anderen stattfindet. Einige der häufigsten Ursachen für Race-Conditions sind Signale, Zugriffsprüfungen und das Öffnen von Dateien. Signale sind von Natur aus asynchrone Ereignisse, deshalb ist besondere Vorsicht im Umgang damit geboten. Das Prüfen des Zugriffs mittels der Aufrufe `access(2)` gefolgt von `open(2)` ist offensichtlich nicht atomar. Benutzer können zwischen den beiden Aufrufen Dateien verschieben. Stattdessen sollten privilegierte Anwendungen `seteuid()` direkt gefolgt von `open()` aufrufen. Auf die gleiche Art sollte eine Anwendung immer eine korrekte Umask vor dem Aufruf von `open()` setzen, um störende Aufrufe von `chmod()` zu umgehen. diff --git a/documentation/content/de/books/handbook/advanced-networking/_index.adoc b/documentation/content/de/books/handbook/advanced-networking/_index.adoc index cca12a36b6..246c7942ad 100644 --- a/documentation/content/de/books/handbook/advanced-networking/_index.adoc +++ b/documentation/content/de/books/handbook/advanced-networking/_index.adoc @@ -1,2833 +1,2833 @@ --- title: Kapitel 31. Weiterführende Netzwerkthemen part: Teil IV. Netzwerke prev: books/handbook/firewalls next: books/handbook/partv showBookMenu: true weight: 36 params: path: "/books/handbook/advanced-networking/" --- [[advanced-networking]] = Weiterführende Netzwerkthemen :doctype: book :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumoffset: 31 :partnums: :source-highlighter: rouge :experimental: :images-path: books/handbook/advanced-networking/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] [[advanced-networking-synopsis]] == Übersicht Dieses Kapitel beschreibt verschiedene weiterführende Netzwerkthemen. Nachdem Sie dieses Kapitel gelesen haben, werden Sie * Die Grundlagen von Gateways und Routen kennen. * Wissen, wie man USB Tethering einrichtet. * Bluetooth(R)- sowie drahtlose, der Norm IEEE(R) 802.11 entsprechende, Geräte mit FreeBSD verwenden können. * Eine Bridge unter FreeBSD einrichten können. * Wissen, wie man mithilfe von PXE über ein Netzwerk von einem NFS Root-Dateisystem bootet. * IPv6 auf einem FreeBSD-Rechner einrichten können. * Das Common Address Redundancy Protocol (CARP) unter FreeBSD einsetzen können. * Wissen, wie VLANs unter FreeBSD konfiguriert werden. * Wissen, wie Bluetooth-Kopfhörer konfiguriert werden. Bevor Sie dieses Kapitel lesen, sollten Sie * Die Grundlagen der [.filename]#/etc/rc#-Skripte verstanden haben. * Mit der grundlegenden Netzwerkterminologie vertraut sein. * Einen neuen FreeBSD-Kernel konfigurieren und installieren können (crossref:kernelconfig[kernelconfig,Konfiguration des FreeBSD-Kernels]). * Wissen, wie man zusätzliche Software von Drittherstellern installiert (crossref:ports[ports,Installieren von Anwendungen: Pakete und Ports]). [[network-routing]] == Gateways und Routen Der Mechanismus mit dem ein Rechner einen Rechner über ein Netzwerk finden kann, wird als _Routing_ bezeichnet. Eine "Route" besteht aus einem definierten Adresspaar: Einem "Ziel" und einem "Gateway". Die Route zeigt an, dass Pakete über das _Gateway_ zum _Ziel_ gelangen können. Es gibt drei Arten von Zielen: Einzelne Rechner (Hosts), Subnetze und das "Standard"ziel. Die "Standardroute" wird verwendet, wenn keine andere Route zutrifft. Außerdem gibt es drei Arten von Gateways: Einzelne Rechner (Hosts), Schnittstellen (Interfaces, auch als "Links" bezeichnet), sowie Ethernet Hardware-Adressen (MAC). Bekannte Adressen werden in einer Routingtabelle gespeichert. Dieser Abschnitt bietet einen Überblick über die Grundlagen des Routings. Er demonstriert, wie ein FreeBSD-System als Router konfiguriert werden kann und bietet einige Tipps zur Fehlerbehebung. [[network-routing-default]] === Grundlagen des Routings man:netstat[1] zeigt die Routingtabellen eines FreeBSD-Systems an: [source,shell] .... % netstat -r Routing tables Internet: Destination Gateway Flags Refs Use Netif Expire default outside-gw UGS 37 418 em0 localhost localhost UH 0 181 lo0 test0 0:e0:b5:36:cf:4f UHLW 5 63288 re0 77 10.20.30.255 link#1 UHLW 1 2421 example.com link#1 UC 0 0 host1 0:e0:a8:37:8:1e UHLW 3 4601 lo0 host2 0:e0:a8:37:8:1e UHLW 0 5 lo0 => host2.example.com link#1 UC 0 0 224 link#1 UC 0 0 .... Die Einträge in diesem Beispiel sind wie folgt: default:: Die erste Route in der Ausgabe gibt die Standardroute (`default`) an. Wenn sich der lokale Rechner mit einem entfernten Rechner verbinden will, wird die Routingtabelle überprüft, um festzustellen, ob bereits ein bekannter Pfad vorhanden ist. Wird für den entfernten Rechner ein Eintrag in der Routingtabelle gefunden, so prüft das System ob es sich über die angegebene Schnittstelle verbinden kann. + Wenn das Zielsystem mit keinem Eintrag übereinstimmt, oder wenn alle bekannten Routen fehlschlagen, verwendet das System die Standardroute. Für die Rechner im lokalen Netzwerk ist das Feld `Gateway` auf das System gesetzt, welches direkt mit dem Internet verbunden ist. `UG` in der Spalte `Flags` zeigt an, dass das Gateway einsatzbereit ist. + Die Standardroute für einen Rechner, der selbst als Gateway zur Außenwelt fungiert, ist der Gateway-Rechner des Internetanbieters (ISP). localhost:: Die zweite Route zeigt die `localhost` Route. Die festgelegte Schnittstelle in der `Netif`-Spalte für `localhost` ist `lo0`, das auch als loopback-Gerät bekannt ist. Das bedeutet, dass der gesamte Datenverkehr für dieses Ziel intern bleibt, anstatt ihn über ein Netzwerk zu versenden. MAC-Adresse:: Bei den mit `0:e0:` beginnenden Adressen handelt es sich um MAC-Adressen. FreeBSD identifiziert Rechner im lokalen Netz, im Beispiel `test0`, automatisch und fügt eine direkte Route über die Ethernet-Schnittstelle [.filename]#re0# zu diesem Rechner hinzu. Außerdem existiert in der Spalte `Expire` ein Timeout, der verwendet wird, wenn dieser Rechner in einem definierten Zeitraum nicht reagiert. Wenn dies passiert, wird die Route zu diesem Rechner automatisch gelöscht. Rechner im lokalen Netz werden über das Routing Information Protocol (RIP) identifiziert, welches den kürzesten Weg zu den jeweiligen Rechnern berechnet. Subnetz:: FreeBSD wird automatisch Subnetzrouten für das lokale Subnetz hinzufügen. In diesem Beispiel ist `10.20.30.255` die Broadcast-Adresse für das Subnetz `10.20.30`, und `example.com` ist der zu diesem Subnetz gehörige Domainname. Das Ziel `link#1` bezieht sich auf die erste Ethernet-Karte im Rechner. + Routen für Rechner im lokalen Netz und lokale Subnetze werden automatisch durch den man:routed[8] Daemon konfiguriert. Ist dieser nicht gestartet, existieren nur statische Routen, die vom Administrator definiert werden. Host:: Die Zeile `host1` bezieht sich auf den Rechner, der durch seine Ethernetadresse bekannt ist. Da es sich um den sendenden Rechner handelt, verwendet FreeBSD automatisch das Loopback-Gerät ([.filename]#lo0#), anstatt den Datenverkehr über die Ethernet-Schnittstelle zu senden. + Die zwei `host2` Zeilen repräsentieren Aliase, die mit man:ifconfig[8] erstellt wurden. Das Symbol `=>` nach der [.filename]#lo0#-Schnittstelle sagt aus, dass zusätzlich zur Loopback-Adresse auch ein Alias eingestellt ist. Solche Routen sind nur auf Rechnern vorhanden, die den Alias bereitstellen. Alle anderen Rechner im lokalen Netz haben für solche Routen nur eine `link#1` Zeile. 224:: Die letzte Zeile (Zielsubnetz `224`) behandelt Multicasting. Schließlich gibt es für Routen noch verschiedene Attribute, die sich in der Spalte `Flags` befinden. <> fasst einige dieser Flags und deren Bedeutung zusammen: [[routeflags]] .Allgemeine Attribute in Routingtabellen [cols="1,1", frame="none", options="header"] |=== | Attribut | Bedeutung |U |Die Route ist aktiv (up). |H |Das Ziel der Route ist ein einzelner Rechner (Host). |G |Alle Daten, die an dieses Ziel gesendet werden, werden von dem Gateway an ihr jeweiliges Ziel weitergeleitet. |S |Diese Route wurde statisch konfiguriert. |C |Erzeugt eine neue Route, basierend auf der Route für den Rechner, mit dem wir uns verbinden. Diese Routenart wird normalerweise für lokale Netzwerke verwendet. |W |Eine Route, die automatisch konfiguriert wurde. Sie basiert auf einer lokalen Netzwerkroute (Clone). |L |Die Route beinhaltet einen Verweis auf eine Ethernetkarte (Link). |=== In FreeBSD kann die Standardroute durch die Angabe der IP-Adresse des Standard-Gateways in [.filename]#/etc/rc.conf# definiert werden: [.programlisting] .... defaultrouter="10.20.30.1" .... Die Standardroute kann mit `route` auch manuell gesetzt werden: [source,shell] .... # route add default 10.20.30.1 .... Beachten Sie, dass manuell hinzugefügte Routen bei einem Neustart des Systems verloren gehen. Weitere Informationen zum Bearbeiten von Netzwerk-Routingtabellen finden Sie in man:route[8]. [[network-static-routes]] === Statische Routen einrichten Ein FreeBSD-System kann als Standard-Gateway bzw. Router für ein Netzwerk konfiguriert werden, wenn es sich um einen Dual-Homed-Host handelt. Ein Dual-Homed-Host ist ein Rechner, der sich in mindestens zwei verschiedenen Netzwerken befindet. Typischerweise ist jedes Netzwerk über eine separate Netzwerkschnittstelle verbunden. Mit IP Aliasing können mehrere Adressen, die jeweils zu einem andren Subnetz gehören, an eine physikalische Schnittstelle gebunden werden. Damit Pakete zwischen den Schnittstellen weitergeleitet werden können, muss das FreeBSD-System als Router konfiguriert werden. Internetstandards und gute Ingenieurspraxis sorgten dafür, dass diese Funktion in FreeBSD in der Voreinstellung deaktiviert ist. Sie kann jedoch aktiviert werden, indem folgende Zeile in [.filename]#/etc/rc.conf# hinzugefügt wird: [.programlisting] .... gateway_enable="YES" # Auf YES setzen, wenn der Rechner als Gateway arbeiten soll .... Um das Routing zu aktivieren, setzen Sie die man:sysctl[8]-Variable `net.inet.ip.forwarding` auf `1`. Um das Routing zu stoppen, muss die Variable wieder auf `0` gesetzt werden. Die Routingtabelle eines Routers benötigt zusätzliche Routen, damit er weiß, wie er andere Netzwerke erreichen kann. Die Routen können entweder manuell als statische Routen hinzugefügt werden, oder aber der Router lernt automatisch die Routen anhand des Routing-Protokolls. Statische Routen eignen sich für kleine Netzwerke und dieser Abschnitt beschreibt, wie Sie eine statische Route für ein kleines Netzwerk hinzufügen. [NOTE] ==== In großen Netzwerken sind statische Routen schlecht skalierbar. FreeBSD beinhaltet den BSD-Routing-Daemon man:routed[8], der die Protokolle RIP (Version 1 und Version 2) sowie IRDP unterstützt. Die Routing-Protokolle BGP und OSPF können über den Port oder das Paket package:net/zebra[] installiert werden. ==== Nehmen wir an, dass wir über folgendes Netzwerk verfügen: image::static-routes.png[] `RouterA`, ein FreeBSD-Rechner, dient als Router für den Zugriff auf das Internet. Die Standardroute ist auf `10.0.0.1` gesetzt, damit ein Zugriff auf das Internet möglich wird. `RouterB` ist bereits konfiguriert, da er `192.168.1.1` als Standard-Gateway benutzt. Bevor die statischen Routen hinzugefügt werden, sieht die Routingtabelle auf `RouterA` in etwa so aus: [source,shell] .... % netstat -nr Routing tables Internet: Destination Gateway Flags Refs Use Netif Expire default 10.0.0.1 UGS 0 49378 xl0 127.0.0.1 127.0.0.1 UH 0 6 lo0 10.0.0/24 link#1 UC 0 0 xl0 192.168.1/24 link#2 UC 0 0 xl1 .... Mit dieser Routingtabelle hat `RouterA` keine Route zum Netzwerk `192.168.2.0/24`. Der folgende Befehl wird das interne Netz 2 in die Routingtabelle von `RouterA` aufnehmen und dabei `192.168.1.2` als nächsten Zwischenschritt (Hop) verwenden: [source,shell] .... # route add -net 192.168.2.0/24 192.168.1.2 .... Ab sofort kann `RouterA` alle Rechner des Netzwerks `192.168.2.0/24` erreichen. Allerdings gehen die Routing-Informationen verloren, wenn das FreeBSD-System neu gestartet wird. Um statische Routen dauerhaft einzurichten, müssen diese in [.filename]#/etc/rc.conf# eingetragen werden: [.programlisting] .... # Add Internal Net 2 as a persistent static route static_routes="internalnet2" route_internalnet2="-net 192.168.2.0/24 192.168.1.2" .... Die Variable `static_routes` enthält eine Reihe von Strings, die durch Leerzeichen getrennt sind. Jeder String bezieht sich auf den Namen einer Route. Die Variable `route__internalnet2_` enthält die statische Route. Wird mit der Variablen `static_routes` mehr als eine Variable angegeben, so werden auch mehrere Routen angelegt. Im folgenden Beispiel werden statische Routen zu den Netzwerken `192.168.0.0/24` und `192.168.1.0/24` angelegt. [.programlisting] .... static_routes="net1 net2" route_net1="-net 192.168.0.0/24 192.168.0.1" route_net2="-net 192.168.1.0/24 192.168.1.1" .... [[network-routing-troubleshooting]] === Problembehandlung Wenn ein Adressraum einem Netzwerk zugeordnet wird, konfiguriert der Dienstanbieter seine Routing-Tabellen, so dass der gesamte Verkehr für das Netzwerk über die Verbindung zu der Seite gesendet wird. Aber woher wissen externe Webseiten, dass sie die Daten an das Netzwerk des ISP senden sollen? Es gibt ein System, das alle zugewiesenen Adressräume verwaltet und die Verbindung zum Internet-Backbone definiert. Der "Backbone" ist das Netz aus Hauptverbindungen, die den Internetverkehr in der ganzen Welt transportieren und verteilen. Jeder Backbone-Rechner verfügt über eine Kopie von Master-Tabellen, die den Verkehr für ein bestimmtes Netzwerk hierarchisch vom Backbone über eine Kette von Dienstanbietern bis hin zu einem bestimmten Netzwerk leiten. Es ist die Aufgabe des Dienstanbieters, den Backbone-Seiten mitzuteilen, dass sie mit einer Seite verbunden wurden. Dieser Vorgang wird als _Bekanntmachung von Routen_ (routing propagation) bezeichnet. Manchmal kommt es zu Problemen bei der Bekanntmachung von Routen, und einige Seiten sind nicht in der Lage, sich zu verbinden. Der vielleicht nützlichste Befehl, um festzustellen wo das Routing nicht funktioniert, ist `traceroute`. Das Programm ist nützlich, falls `ping` fehlschlägt. Rufen Sie `traceroute` mit dem Namen des entfernten Rechners auf, mit dem eine Verbindung aufgebaut werden soll. Die Ausgabe zeigt die Gateway-Rechner entlang des Verbindungspfades an. Schließlich wird der Zielrechner erreicht oder es kommt zu einem Verbindungsabbruch. Weitere Informationen finden Sie in man:traceroute[8]. [[network-routing-multicast]] === Multicast-Routing FreeBSD unterstützt sowohl Multicast-Anwendungen als auch Multicast-Routing. Multicast-Anwendungen benötigen keine spezielle Konfiguration, um auf FreeBSD lauffähig zu sein. Damit Multicast-Routing unterstützt wird, muss die folgende Option in der Kernelkonfiguration aktiviert werden: [.programlisting] .... options MROUTING .... Der Multicast-Routing-Daemon mrouted kann als Port oder Paket package:net/mroute[] installiert werden. Dieser Daemon implementiert das DVMRP Multicast-Routing-Protokoll. Um die Tunnel und DVMRP einzurichten, muss [.filename]#/usr/local/etc/mrouted.conf# bearbeitet werden. Bei der Installation von mrouted wird auch map-mbone und mrinfo sowie die zugehörigen Manualpages installiert, in denen Sie auch Konfigurationsbeispiele finden können. [NOTE] ==== DVMRP wurde in vielen Multicast-Installationen weitgehend durch das PIM-Protokoll ersetzt. Weitere Informationen finden Sie in man:pim[4]. ==== [[network-wireless]] == Drahtlose Netzwerke === Grundlagen Die meisten drahtlosen Netzwerke basieren auf dem Standard IEEE(R) 802.11. Ein einfaches drahtloses Netzwerk besteht aus Stationen, die im 2,4 GHz- oder im 5 GHz-Band miteinander kommunizieren. Es ist aber auch möglich, dass regional andere Frequenzen, beispielsweise im 2,3 GHz- oder 4,9 GHz-Band, verwendet werden. 802.11-Netzwerke können auf zwei verschiedene Arten aufgebaut sein: Im _Infrastruktur-Modus_ agiert eine Station als Master, mit dem sich alle anderen Stationen verbinden. Die Summe aller Stationen wird als Basic Service Set (BSS), die Master-Station hingegen als Access Point (AP) bezeichnet. In einem BSS läuft jedwede Kommunikation über den Access Point. Die zweite Form drahtloser Netzwerke sind die sogenannten _Ad-hoc-Netzwerke_ (auch als IBSS bezeichnet), in denen es keinen Access Point gibt und in denen die Stationen direkt miteinander kommunizieren. Die ersten 802.11-Netzwerke arbeiteten im 2,4 GHz-Band und nutzten dazu Protokolle der IEEE(R)-Standards 802.11 sowie 802.11b. Diese Standards legen unter anderem Betriebsfrequenzen sowie Merkmale des MAC-Layers (wie Frames und Transmissionsraten) fest. Später kam der Standard 802.11a hinzu, der im 5 GHz-Band, im Gegensatz zu den ersten beiden Standards aber mit unterschiedlichen Signalmechanismen und höheren Transmissionsraten arbeitet. Der neueste Standard 802.11g implementiert die Signal- und Transmissionsmechanismen von 802.11a im 2,4 GHz-Band, ist dabei aber abwärtskompatibel zu 802.11b-Netzwerken. Unabhängig von den zugrundeliegenden Transportmechanismen verfügen 802.11-Netzwerke über diverse Sicherheitsmechanismen. Der ursprüngliche 802.11-Standard definierte lediglich ein einfaches Sicherheitsprotokoll namens WEP. Dieses Protokoll verwendet einen fixen, gemeinsam verwendeten Schlüssel sowie die RC4-Kryptografie-Chiffre, um Daten verschlüsselt über das drahtlose Netzwerk zu senden. Alle Stationen des Netzwerks müssen sich auf den gleichen fixen Schlüssel einigen, um miteinander kommunizieren zu können. Dieses Schema ist sehr leicht zu knacken und wird deshalb heute kaum mehr eingesetzt. Aktuelle Sicherheitsmechanismen bauen auf dem Standard IEEE(R) 802.11i auf, der neue kryptographische Schlüssel (Chiffren), ein neues Protokoll für die Anmeldung von Stationen an einem Access Point, sowie Mechanismen zum Austausch von Schlüsseln als Vorbereitung der Kommunikation zwischen verschiedenen Geräten festlegt. Kryptografische Schlüssel werden in regelmäßigen Abständen aktualisiert. Außerdem gibt es Mechanismen zur Feststellung und Prävention von Einbruchsversuchen. Ein weiteres häufig verwendetes Sicherheitsprotokoll ist WPA. Dabei handelt es sich um einen Vorläufer von 802.11i, der von einem Industriekonsortium als Zwischenlösung bis zur endgültigen Verabschiedung von 802.11i entwickelt wurde. WPA definiert eine Untergruppe der Anforderungen des 802.11i-Standards und ist für den Einsatz in älterer Hardware vorgesehen. WPA benötigt nur den TKIP-Chiffre, welcher auf dem ursprünglichen WEP-Code basiert. 802.11i erlaubt zwar auch die Verwendung von TKIP, benötigt aber zusätzlich eine stärkere Chiffre (AES-CCM) für die Datenverschlüsselung. AES war für WPA nicht vorgesehen, weil man es als zu rechenintensiv für den Einsatz in älteren Geräten ansah. Ein weiterer zu beachtender Standard ist 802.11e. Dieser definiert Protokolle zur Übertragung von Multimedia-Anwendungen, wie das Streaming von Videodateien oder Voice-over-IP (VoIP) in einem 802.11-Netzwerk. Analog zu 802.11i verfügt auch 802.11e über eine vorläufige Spezifikation namens WMM (ursprünglich WME), die von einem Industriekonsortium als Untergruppe von 802.11e spezifiziert wurde, um Multimedia-Anwendungen bereits vor der endgültigen Verabschiedung des 802.11e-Standards implementieren zu können. 802.11e sowie WME/WMM erlauben eine Prioritätenvergabe beim Datentransfer in einem drahtlosen Netzwerk. Möglich wird dies durch den Einsatz von Quality of Service-Protokollen (QoS) und erweiterten Medienzugriffsprotokollen. Werden diese Protokolle korrekt implementiert, erlauben sie hohe Datenübertragungsraten und einen priorisierten Datenfluss. FreeBSD unterstützt die Standards 802.11a, 802.11b und 802.11g. Ebenfalls unterstützt werden WPA sowie die Sicherheitsprotokolle gemäß 802.11i (sowohl für 11a, 11b als auch 11g). QoS und Verkehrspriorisierung, die von den WME/WMM-Protokollen benötigt werden, werden für einen begrenzten Satz von drahtlosen Geräten unterstützt. [[network-wireless-quick-start]] === Schnellstartanleitung Häufig soll ein Computer an ein vorhandenes Drahtlosnetzwerk angeschlossen werden. Diese Prozedur zeigt die dazu erforderlichen Schritte. [.procedure] . Besorgen Sie sich vom Netzwerkadministrator die SSID (Service Set Identifier) und den PSK (Pre Shared Key) für das Drahtlosnetzwerk. . Ermitteln Sie den drahtlosen Adapter. Der [.filename]#GENERIC#-Kernel von FreeBSD enthält Treiber für viele gängige Adapter. Wenn der drahtlose Adapter eines dieser Modelle ist, wird das in der Ausgabe von man:ifconfig[8] angezeigt: + [source,shell] .... % ifconfig | grep -B3 -i wireless .... -+ ++ In FreeBSD 11 und neueren Versionen verwenden Sie stattdessen diesen Befehl: + [source,shell] .... % sysctl net.wlan.devices .... -+ ++ Wenn der drahtlose Adapter nicht aufgeführt wird, könnte ein zusätzliches Kernelmodul erforderlich sein. Es besteht jedoch auch die Möglichkeit, dass der Adapter von FreeBSD nicht unterstützt wird. -+ ++ Dieses Beispiel verwendet einen drahtlosen Atheros-Adapter `ath0`. . Fügen Sie in [.filename]#/etc/wpa_supplicant.conf# einen Eintrag für das Netzwerk hinzu. Wenn die Datei nicht existiert, müssen Sie diese erstellen. Ersetzen Sie _myssid_ und _psk_ durch die SSID und den PSK. Diese Informationen werden vom Netzwerkadministrator zur Verfügung gestellt. + [.programlisting] .... network={ ssid="myssid" psk="mypsk" } .... . Fügen Sie die entsprechenden Einträge in [.filename]#/etc/rc.conf# ein, um das Netzwerk beim Start zu konfigurieren: + [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="WPA SYNCDHCP" .... . Starten Sie den Computer oder den Netzwerkdienst neu, um sich mit dem Netzwerk zu verbinden: + [source,shell] .... # service netif restart .... [[network-wireless-basic]] === Basiskonfiguration ==== Kernelkonfiguration Um ein drahtloses Netzwerk zu nutzen, wird eine drahtlose Netzwerkkarte benötigt und ein Kernel, der drahtlose Netzwerke unterstützt. Der Kernel unterstützt den Einsatz von Kernelmodulen. Daher muss nur die Unterstützung für die verwendeten Geräte aktiviert werden. Die meisten drahtlosen Geräte verwenden Bauteile von Atheros und werden deshalb vom man:ath[4]-Treiber unterstützt. Um diesen Treiber zu verwenden, muss die folgende Zeile in [.filename]#/boot/loader.conf# hinzugefügt werden: [.programlisting] .... if_ath_load="YES" .... Der Atheros-Treiber besteht aus drei Teilen: dem Treiber selbst (man:ath[4]), dem Hardware-Support-Layer für die chip-spezifischen Funktionen (man:ath_hal[4]) sowie einem Algorithmus zur Auswahl der Frame-Übertragungsrate (ath_rate_sample). Wenn diese Unterstützung als Kernelmodul geladen wird, kümmert sich das Modul automatisch um Abhängigkeiten. Um die Unterstützung für ein anderes drahtloses Gerät zu laden, geben Sie das entsprechende Modul für dieses Gerät an. Dieses Beispiel zeigt die Verwendung von Geräten, die auf Bauteilen von Intersil Prism basieren und den Treiber man:wi[4] benötigen: [.programlisting] .... if_wi_load="YES" .... [NOTE] ==== Die Beispiele in diesem Abschnitt verwenden den man:ath[4]-Treiber. Verwenden Sie ein anderes Gerät, muss der Gerätename an die Konfiguration angepasst werden. Eine Liste aller verfügbaren Treiber und unterstützten drahtlosen Geräte finden sich in den FreeBSD Hardware Notes unter https://www.FreeBSD.org/releases/[Release Information] der FreeBSD Homepage. Gibt es keinen nativen FreeBSD-Treiber für das drahtlose Gerät, kann möglicherweise mit crossref:config[config-network-ndis,NDIS] ein Windows(R)-Treiber verwendet werden. ==== Zusätzlich müssen die Module zur Verschlüsselung des drahtlosen Netzwerks geladen werden. Diese werden normalerweise dynamisch vom man:wlan[4]-Modul geladen. Im folgenden Beispiel erfolgt allerdings eine manuelle Konfiguration. Folgende Module sind verfügbar: man:wlan_wep[4], man:wlan_ccmp[4] und man:wlan_tkip[4]. Sowohl man:wlan_ccmp[4] als auch man:wlan_tkip[4] werden nur benötigt, wenn WPA und/oder die Sicherheitsprotokolle von 802.11i verwendet werden. Wenn das Netzwerk keine Verschlüsselung verwendet, wird die man:wlan_wep[4]-Unterstützung nicht benötigt. Um diese Module beim Systemstart zu laden, fügen Sie folgende Zeilen in [.filename]#/boot/loader.conf# ein: [.programlisting] .... wlan_wep_load="YES" wlan_ccmp_load="YES" wlan_tkip_load="YES" .... Sobald diese Einträge in [.filename]#/boot/loader.conf# vorhanden sind, muss das FreeBSD-System neu gestartet werden. Alternativ können die Kernelmodule auch manuell mit man:kldload[8] geladen werden. [NOTE] ==== Benutzer, die keine Kernelmodule verwenden wollen, können die benötigten Treiber auch in den Kernel kompilieren. Dazu müssen die folgenden Zeilen in die Kernelkonfigurationsdatei aufgenommen werden: [.programlisting] .... device wlan # 802.11 support device wlan_wep # 802.11 WEP support device wlan_ccmp # 802.11 CCMP support device wlan_tkip # 802.11 TKIP support device wlan_amrr # AMRR transmit rate control algorithm device ath # Atheros pci/cardbus NIC's device ath_hal # pci/cardbus chip support options AH_SUPPORT_AR5416 # enable AR5416 tx/rx descriptors device ath_rate_sample # SampleRate tx rate control for ath .... Mit diesen Informationen in der Kernelkonfigurationsdatei kann der Kernel neu gebaut, und das FreeBSD-System anschließend neu gestartet werden. ==== Informationen über das drahtlose Gerät sollten in den Boot-Meldungen folgendermaßen angezeigt werden: [source,shell] .... ath0: mem 0x88000000-0x8800ffff irq 11 at device 0.0 on cardbus1 ath0: [ITHREAD] ath0: AR2413 mac 7.9 RF2413 phy 4.5 .... ==== Konfiguration der entsprechenden Region Da die rechtliche Situation in verschiedenen Teilen der Welt unterschiedlich ist, ist es notwendig, die für Ihre Region geltenden Domänen korrekt einzustellen, um die richtigen Informationen darüber zu erhalten, welche Kanäle benutzt werden können. Die verfügbaren Definitionen der Regionen finden Sie in [.filename]#/etc/regdomain.xml#. Um die Daten zur Laufzeit einzustellen, benutzen Sie `ifconfig`: [source,shell] .... # ifconfig wlan0 regdomain ETSI country AT .... Um die Einstellungen beizubehalten, fügen Sie folgende Zeile in [.filename]#/etc/rc.conf# hinzu: [source,shell] .... # sysrc create_args_wlan0="country AT regdomain ETSI" .... === Infrastruktur-Modus Drahtlose Netzwerke werden in der Regel im Infrastruktur-Modus (BSS) betrieben. Dazu werden mehrere drahtlose Access Points zu einem gemeinsamen drahtlosen Netzwerk verbunden. Jedes dieser drahtlosen Netzwerke hat einen eigenen Namen, der als >SSID> bezeichnet wird. Alle Clients eines drahtlosen Netzwerks verbinden sich in diesem Modus mit einem Access Point. ==== FreeBSD-Clients ===== Einen Access Point finden Um nach verfügbaren drahtlosen Netzwerken zu suchen verwenden Sie man:ifconfig[8]. Dieser Scanvorgang kann einen Moment dauern, da jede verfügbare Frequenz auf verfügbare Access Points hin überprüft werden muss. Nur der Super-User kann einen Scanvorgang starten: [source,shell] .... # ifconfig wlan0 create wlandev ath0 # ifconfig wlan0 up scan SSID/MESH ID BSSID CHAN RATE S:N INT CAPS dlinkap 00:13:46:49:41:76 11 54M -90:96 100 EPS WPA WME freebsdap 00:11:95:c3:0d:ac 1 54M -83:96 100 EPS WPA .... [NOTE] ==== Die Netzwerkkarte muss in den Status `up` versetzt werden, bevor der erste Scanvorgang gestartet werden kann. Für spätere Scans ist dies aber nicht mehr erforderlich. ==== Als Ergebnis erhalten Sie eine Liste mit allen gefundenen BSS/IBSS-Netzwerken. Zusätzlich zum Namen des Netzwerks, der `SSID`, wird auch die `BSSID` ausgegeben. Dabei handelt es sich um die MAC-Adresse des Access Points. Das Feld `CAPS` gibt den Typ des Netzwerks sowie die Fähigkeiten der Stationen innerhalb des Netzwerks an: .Station Capability Codes [cols="1,1", frame="none", options="header"] |=== | Capability Code | Bedeutung |`E` |Extended Service Set (ESS). Zeigt an, dass die Station Teil eines Infrastruktur-Netzwerks ist, und nicht eines IBSS/Ad-hoc-Netzwerks. |`I` |IBSS/Ad-hoc-Netzwerk. Die Station ist Teil eines Ad-hoc-Netzwerks und nicht eines ESS-Netzwerks. |`P` |Privacy. Alle Datenframes, die innerhalb des BSS ausgetauscht werden, sind verschlüsselt. Dieses BSS verwendet dazu kryptographische Verfahren wie WEP, TKIP oder AES-CCMP. |`S` |Short Preamble. Das Netzwerk verwendet eine kurze Präambel (definiert in 802.11b High Rate/DSSS PHY). Eine kurze Präambel verwendet ein 56 Bit langes Sync-Feld, im Gegensatz zu einer langen Präambel, die ein 128 Bit langes Sync-Feld verwendet. |`s` |Short slot time. Das 802.11g-Netzwerk verwendet eine kurze Slotzeit, da es in diesem Netzwerk keine veralteten (802.11b) Geräte gibt. |=== Um eine Liste der bekannten Netzwerke auszugeben, verwenden Sie den folgenden Befehl: [source,shell] .... # ifconfig wlan0 list scan .... Diese Liste kann entweder automatisch durch das drahtlose Gerät oder manuell durch eine `scan`-Aufforderung aktualisiert werden. Veraltete Informationen werden dabei automatisch entfernt. ===== Basiseinstellungen Dieser Abschnitt beschreibt, wie Sie eine drahtlose Netzwerkkarte ohne Verschlüsselung unter FreeBSD einrichten. Nachdem Sie sich mit den Informationen dieses Abschnitts vertraut gemacht haben, sollten Sie das drahtlose Netzwerk mit <> verschlüsseln. Das Einrichten eines drahtlosen Netzwerks erfolgt in drei Schritten: Der Auswahl eines Access Points, die Anmeldung der Station sowie der Konfiguration der IP-Adresse. ====== Einen Access Point auswählen Im Normalfall wird sich die Station automatisch mit einem der zur Verfügung stehenden Access Points verbinden. Dazu muss lediglich das drahtlose Gerät aktiviert, oder in [.filename]#/etc/rc.conf# eingetragen sein: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="DHCP" .... Stehen mehrere Access Points zur Verfügung, kann ein spezifischer durch Angabe der SSID gewählt werden: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="ssid Ihre_SSID DHCP" .... Gibt es in einem Netzwerk mehrere Access Points mit der gleichen SSID, was das Routing vereinfacht, kann es notwendig sein, dass ein bestimmtes Gerät verbunden werden muss. Dazu muss lediglich die BSSID des Access Points angeben werden. Die Angabe der SSID ist hierbei nicht zwingend notwendig: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="ssid Ihre_SSID bssid xx:xx:xx:xx:xx:xx DHCP" .... Es gibt noch weitere Möglichkeiten, den Zugriff auf bestimmte Access Point zu beschränken, beispielsweise durch die Begrenzung der Frequenzen, auf denen eine Station nach einem Access Point sucht. Sinnvoll ist ein solches Vorgehen beispielsweise, wenn das drahtlose Gerät in verschiedenen Frequenzbereichen arbeiten kann, da in diesem Fall das Prüfen aller Frequenzen sehr zeitintensiv sein kann. Um nur innerhalb eines bestimmten Frequenzbereichs nach einem Access Point zu suchen, verwenden Sie die Option `mode`: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="mode 11g ssid Ihre_SSID DHCP" .... In diesem Beispiel sucht das drahtlose Gerät nur im 2,4 GHz-Band (802.11g), aber nicht innerhalb des 5 GHz-Bandes nach einem Access Point. Mit der Option `channel` kann eine bestimmte Frequenz vorgegeben werden, auf der gesucht werden soll. Die Option `chanlist` erlaubt die Angabe mehrerer erlaubter Frequenzen. Eine umfassende Beschreibung dieser Optionen finden Sie in man:ifconfig[8]. ====== Authentifizierung Sobald ein Access Point gefunden wurde, muss sich die Station am Access Point authentifizieren, bevor Daten übertragen werden können. Dazu gibt es verschiedene Möglichkeiten. Am häufigsten wird die sogenannte _offene Authentifizierung_ verwendet. Dabei wird es jeder Station erlaubt, sich mit einem Netzwerk zu verbinden und Daten zu übertragen. Aus Sicherheitsgründen sollte diese Methode allerdings nur zu Testzwecken bei der erstmaligen Einrichtung eines drahtlosen Netzwerks verwendet werden. Andere Authentifizierungsmechanismen erfordern den Austausch kryptographischer Informationen, bevor sie die Übertragung von Daten erlauben. Dazu gehören der Austausch fixer (vorher vereinbarter) Schlüssel oder Kennwörter, sowie der Einsatz komplexerer Verfahren mit Backend-Diensten wie RADIUS. Die offene Authentifizierung ist die Voreinstellung. Am zweithäufigsten kommt das im <> beschriebene WPA-PSK zum Einsatz, welches auch als WPA Personal bezeichnet wird. [NOTE] ==== Kommt eine Apple(R) AirPort(R) Extreme-Basisstation als Access Point zum Einsatz, muss sowohl die Shared-Key-Authentifizierung als auch ein WEP-Schlüssel konfiguriert werden. Die entsprechende Konfiguration erfolgt entweder in [.filename]#/etc/rc.conf# oder über das Programm man:wpa_supplicant[8]. Für eine einzelne AirPort(R)-Basisstation kann der Zugriff wie folgt konfiguriert werden: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="authmode shared wepmode on weptxkey 1 wepkey 01234567 DHCP" .... Normalerweise sollte Shared-Key-Authentifizierung nicht verwendet werden, da diese die Sicherheit des WEP-Schlüssel noch weiter verringert. Wenn WEP für Kompatibilität mit älteren Geräten verwendet werden muss, ist es besser, WEP mit offener Authentifizierung zu verwenden. Weitere Informationen zu WEP finden Sie im <>. ==== ====== Eine IP-Adresse über DHCP beziehen Sobald ein Access Point ausgewählt ist und die Authentifizierungsparameter festgelegt sind, wird eine IP-Adresse benötigt. In der Regel wird die IP-Adresse über DHCP bezogen. Um dies zu erreichen, bearbeiten Sie [.filename]#/etc/rc.conf# und fügen Sie `DHCP` für das drahtlose Gerät in die Konfiguration hinzu: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="DHCP" .... Das drahtlose Gerät kann nun gestartet werden: [source,shell] .... # service netif start .... Nachdem das Gerät aktiviert wurde, kann mit man:ifconfig[8] der Status des Geräts [.filename]#ath0# abgefragt werden: [source,shell] .... # ifconfig wlan0 wlan0: flags=8843 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.1.100 netmask 0xffffff00 broadcast 192.168.1.255 media: IEEE 802.11 Wireless Ethernet OFDM/54Mbps mode 11g status: associated ssid dlinkap channel 11 (2462 Mhz 11g) bssid 00:13:46:49:41:76 country US ecm authmode OPEN privacy OFF txpower 21.5 bmiss 7 scanvalid 60 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst .... `status: associated` besagt, dass sich das Gerät mit dem drahtlosen Netzwerk verbunden hat. `bssid 00:13:46:49:41:76` ist die MAC-Adresse des Access Points und `authmode OPEN` zeigt an, dass die Kommunikation nicht verschlüsselt wird. ====== Statische IP-Adressen Wenn eine IP-Adresse nicht von einem DHCP-Server bezogen werden kann, vergeben Sie eine statische IP-Adresse. Ersetzten Sie dazu das oben gezeigte Schlüsselwort `DHCP` durch die entsprechende IP-Adresse. Beachten Sie dabei, dass Sie die anderen Konfigurationsparameter nicht versehentlich verändern: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="inet 192.168.1.100 netmask 255.255.255.0 ssid your_ssid_here" .... [[network-wireless-wpa]] ===== WPA Wi-Fi Protected Access (WPA) ist ein Sicherheitsprotokoll, das in 802.11-Netzwerken verwendet wird, um die fehlende Authentifizierung und Schwächen von WEP zu vermeiden. WPA stellt das aktuelle 802.1X-Authentifizierungsprotokoll dar und verwendet eine von mehreren Chiffren, um die Datensicherheit zu gewährleisten. Die einzige Chiffre, die von WPA verlangt wird, ist Temporary Key Integrity Protocol (TKIP). TKIP ist eine Chiffre, die die von WEP verwendete RC4-Chiffre um Funktionen zur Prüfung der Datenintegrität und zur Erkennung und Bekämpfung von Einbruchsversuchen erweitert. TKIP ist durch Softwaremodifikationen auch unter veralteter Hardware lauffähig. Im Vergleich zu WEP ist WPA zwar sehr viel sicherer, es ist aber dennoch nicht völlig immun gegen Angriffe. WPA definiert mit AES-CCMP noch eine weitere Chiffre als Alternative zu TKIP. AES-CCMP, welches häufig als WPA2 oder RSN bezeichnet wird, sollte bevorzugt eingesetzt werden. WPA definiert Authentifizierungs- und Verschlüsselungsprotokolle. Die Authentifizierung erfolgt in der Regel über eine der folgenden Techniken: 802.1X gemeinsam mit einem Backend-Authentifizierungsdienst wie RADIUS, oder durch einen Minimal-Handshake zwischen der Station und dem Access Point mit einem vorher vereinbarten gemeinsamen Schlüssel. Die erste Technik wird als WPA Enterprise, die zweite hingegen als WPA Personal bezeichnet. Da sich der Aufwand für das Aufsetzen eines RADIUS-Backend-Servers für die meisten drahtlosen Netzwerke nicht lohnt, wird WPA in der Regel als WPA-PSK konfiguriert. Die Kontrolle der drahtlosen Verbindung sowie das Aushandeln des Schlüssel, oder die Authentifizierung mit einem Server, erfolgt über man:wpa_supplicant[8]. Dieses Programm benötigt eine Konfigurationsdatei, [.filename]#/etc/wpa_supplicant.conf#. Weitere Informationen finden Sie in man:wpa_supplicant.conf[5]. [[network-wireless-wpa-wpa-psk]] ====== WPA-PSK WPA-PSK, das auch als WPA-Personal bekannt ist, basiert auf einem gemeinsamen, vorher vereinbarten Schlüssel (PSK), der aus einem Passwort generiert und danach als Master-Key des drahtlosen Netzwerks verwendet wird. Jeder Benutzer des drahtlosen Netzwerks verwendet daher _den gleichen_ Schlüssel. WPA-PSK sollte nur in kleinen Netzwerken eingesetzt werden, in denen die Konfiguration eines Authentifizierungsservers nicht möglich oder erwünscht ist. [WARNING] ==== Achten Sie darauf, immer starke Passwörter zu verwenden, die ausreichend lang sind und auch Sonderzeichen enthalten, damit diese nicht leicht erraten oder umgangen werden können. ==== Der erste Schritt zum Einsatz von WPA-PSK ist die Konfiguration der SSID und des gemeinsamen Schlüssels des Netzwerks in [.filename]#/etc/wpa_supplicant.conf#: [.programlisting] .... network={ ssid="freebsdap" psk="freebsdmall" } .... Danach wird in [.filename]#/etc/rc.conf# definiert, dass WPA zur Verschlüsselung eingesetzt werden soll und dass die IP-Adresse über DHCP bezogen wird: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="WPA DHCP" .... Nun kann das drahtlose Gerät aktiviert werden: [source,shell] .... # service netif start Starting wpa_supplicant. DHCPDISCOVER on wlan0 to 255.255.255.255 port 67 interval 5 DHCPDISCOVER on wlan0 to 255.255.255.255 port 67 interval 6 DHCPOFFER from 192.168.0.1 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 DHCPACK from 192.168.0.1 bound to 192.168.0.254 -- renewal in 300 seconds. wlan0: flags=8843 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.254 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet OFDM/36Mbps mode 11g status: associated ssid freebsdap channel 1 (2412 MHz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 3:128-bit txpower 21.5 bmiss 7 scanvalid 450 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst roaming MANUAL .... Alternativ kann das drahtlose Gerät manuell, mit Hilfe der Informationen aus [.filename]#/etc/wpa_supplicant.conf# konfiguriert werden: [source,shell] .... # wpa_supplicant -i wlan0 -c /etc/wpa_supplicant.conf Trying to associate with 00:11:95:c3:0d:ac (SSID='freebsdap' freq=2412 MHz) Associated with 00:11:95:c3:0d:ac WPA: Key negotiation completed with 00:11:95:c3:0d:ac [PTK=CCMP GTK=CCMP] CTRL-EVENT-CONNECTED - Connection to 00:11:95:c3:0d:ac completed (auth) [id=0 id_str=] .... Im zweiten Schritt starten Sie nun man:dhclient[8], um eine IP-Adresse vom DHCP-Server zu beziehen: [source,shell] .... # dhclient wlan0 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 DHCPACK from 192.168.0.1 bound to 192.168.0.254 -- renewal in 300 seconds. # ifconfig wlan0 wlan0: flags=8843 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.254 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet OFDM/36Mbps mode 11g status: associated ssid freebsdap channel 1 (2412 MHz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 3:128-bit txpower 21.5 bmiss 7 scanvalid 450 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst roaming MANUAL .... [NOTE] ==== Enthält [.filename]#/etc/rc.conf# bereits die Zeile `ifconfig_wlan0="DHCP"`, wird man:dhclient[8] automatisch gestartet, nachdem man:wpa_supplicant[8] sich mit dem Access Point verbunden hat. ==== Sollte der Einsatz von DHCP nicht möglich oder nicht gewünscht sein, konfigurieren Sie eine statische IP-Adresse, nachdem man:wpa_supplicant[8] die Station authentifiziert hat: [source,shell] .... # ifconfig wlan0 inet 192.168.0.100 netmask 255.255.255.0 # ifconfig wlan0 wlan0: flags=8843 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.100 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet OFDM/36Mbps mode 11g status: associated ssid freebsdap channel 1 (2412 MHz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 3:128-bit txpower 21.5 bmiss 7 scanvalid 450 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst roaming MANUAL .... Falls DHCP nicht verwendet wird, müssen zusätzlich noch das Standard-Gateway sowie der Nameserver manuell festgelegt werden: [source,shell] .... # route add default your_default_router # echo "nameserver your_DNS_server" >> /etc/resolv.conf .... [[network-wireless-wpa-eap-tls]] ====== WPA und EAP-TLS Die zweite Möglichkeit, WPA einzusetzen, ist die Verwendung eines 802.1X-Backend-Authentifizierungsservers. Diese Variante wird als WPA-Enterprise bezeichnet, um sie vom weniger sicheren WPA-Personal abzugrenzen. Die bei WPA-Enterprise verwendete Authentifizierung basiert auf dem Extensible Authentication Protocol (EAP). EAP selbst bietet keine Verschlüsselung, sondern operiert in einem verschlüsselten Tunnel. Es gibt verschiedene auf EAP basierende Authentifizierungsmethoden, darunter EAP-TLS, EAP-TTLS und EAP-PEAP. EAP mit Transport Layers Security (EAP-TLS) ist ein sehr gut unterstütztes Authentifizierungsprotokoll, da es sich dabei um die erste EAP-Methode handelt, die von der http://www.wi-fi.org/[ Wi-Fi Alliance] zertifiziert wurde. EAP-TLS erfordert drei Zertifikate: Das auf allen Rechnern installierte CA-Zertifikat, das Server-Zertifikat des Authentifizierungsservers, sowie ein Client-Zertifikat für jeden drahtlosen Client. Sowohl der Authentifizierungsservers als auch die drahtlosen Clients authentifizieren sich gegenseitig über Zertifikate, wobei sie überprüfen, ob diese Zertifikate auch von der Zertifizierungs-Authorität (CA) des jeweiligen Unternehmens signiert wurden. Die Konfiguration erfolgt (analog zu WPA-PSK) über [.filename]#/etc/wpa_supplicant.conf#: [.programlisting] .... network={ ssid="freebsdap" <.> proto=RSN <.> key_mgmt=WPA-EAP <.> eap=TLS <.> identity="loader" <.> ca_cert="/etc/certs/cacert.pem" <.> client_cert="/etc/certs/clientcert.pem" <.> private_key="/etc/certs/clientkey.pem" <.> private_key_passwd="freebsdmallclient" <.> } .... <.> Der Name des Netzwerks (SSID). <.> Das als WPA2 bekannte RSN IEEE(R) 802.11i Protokoll wird verwendet. <.> Die `key_mgmt`-Zeile bezieht sich auf das verwendete Key-Management-Protokoll. In diesem Beispiel wird WPA gemeinsam mit der EAP-Authentifizierung verwendet. <.> Die für die Verbindung verwendete EAP-Methode. <.> Das `identity`-Feld enthält den von EAP verwendeten Identifizierungsstring. <.> Das Feld `ca_cert` gibt den Pfad zum CA-Zertifikat an. Diese Datei wird zur Verifizierung des Server-Zertifikats benötigt. <.> Die `client_cert`-Zeile gibt den Pfad zum Client-Zertifikat an. Jeder Client hat ein eigenes, innerhalb des Netzwerks eindeutiges, Zertifikat. <.> Das Feld `private_key` gibt den Pfad zum privaten Schlüssel des Client-Zertifikat an. <.> Das Feld `private_key_passwd` enthält die Passphrase für den privaten Schlüssel. Danach fügen Sie die folgende Zeile in [.filename]#/etc/rc.conf# ein: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="WPA DHCP" .... Nun können Sie das drahtlose Gerät aktivieren: [source,shell] .... # service netif start Starting wpa_supplicant. DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 7 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 15 DHCPACK from 192.168.0.20 bound to 192.168.0.254 -- renewal in 300 seconds. wlan0: flags=8843 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.254 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet DS/11Mbps mode 11g status: associated ssid freebsdap channel 1 (2412 MHz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 3:128-bit txpower 21.5 bmiss 7 scanvalid 450 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst roaming MANUAL .... Alternativ kann das drahtlose Gerät manuell mit man:wpa_supplicant[8] und man:ifconfig[8] aktiviert werden. [[network-wireless-wpa-eap-ttls]] ====== WPA mit EAP-TTLS Bei EAP-TLS müssen sowohl der Authentifizierungsserver als auch die Clients jeweils ein eigenes Zertifikat aufweisen. Bei EAP-TTLS ist das Client-Zertifikat optional. EAP-TTLS geht dabei vor wie ein Webserver, der einen sicheren SSL-Tunnel erzeugen kann, ohne dass der Besucher dabei über ein clientseitiges Zertifikat verfügen muss. EAP-TTLS verwendet einen verschlüsselten TLS-Tunnel zum sicheren Transport der Authentifizierungsdaten. Die erforderliche Konfiguration erfolgt in [.filename]#/etc/wpa_supplicant.conf#: [.programlisting] .... network={ ssid="freebsdap" proto=RSN key_mgmt=WPA-EAP eap=TTLS <.> identity="test" <.> password="test" <.> ca_cert="/etc/certs/cacert.pem" <.> phase2="auth=MD5" <.> } .... <.> Die für die Verbindung verwendete EAP-Methode. <.> Das `identity`-Feld enthält den Identifizierungsstring für die EAP-Authentifizierung innerhalb des verschlüsselten TLS-Tunnels. <.> Das `password`-Feld enthält die Passphrase für die EAP-Authentifizierung. <.> Das Feld `ca_cert` gibt den Pfad zum CA-Zertifikat an. Diese Datei wird zur Verifizierung des Server-Zertifikats benötigt. <.> Die innerhalb des verschlüsselten TLS-Tunnels verwendete Authentifizierungsmethode. In Fall von PEAP ist dies `auth=MSCHAPV2`. Folgende Zeilen müssen in [.filename]#/etc/rc.conf# aufgenommen werden: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="WPA DHCP" .... Nun kann das drahtlose Gerät aktiviert werden: [source,shell] .... # service netif start Starting wpa_supplicant. DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 7 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 15 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 21 DHCPACK from 192.168.0.20 bound to 192.168.0.254 -- renewal in 300 seconds. wlan0: flags=8843 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.254 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet DS/11Mbps mode 11g status: associated ssid freebsdap channel 1 (2412 MHz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 3:128-bit txpower 21.5 bmiss 7 scanvalid 450 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst roaming MANUAL .... [[network-wireless-wpa-eap-peap]] ====== WPA mit EAP-PEAP [NOTE] ==== PEAPv0/EAP-MSCHAPv2 ist die gängigste PEAP-Methode. In diesem Kapitel wird der Begriff PEAP stellvertretend für diese Methode verwendet. ==== Protected EAP (PEAP) wurde als Alternative zu EAP-TTLS entwickelt und ist nach EAP-TLS der meist genutzte EAP-Standard. In einem Netzwerk mit verschiedenen Betriebssystemen sollte PEAP das am besten unterstützte Standard nach EAP-TLS sein. PEAP arbeitet ähnlich wie EAP-TTLS. Es verwendet ein serverseitiges Zertifikat, um einen verschlüsselten TLS-Tunnel, über den die sichere Authentifizierung zwischen den Clients und dem Authentifizierungsserver erfolgt. In Sachen Sicherheit unterscheiden sich EAP-TTLS und PEAP allerdings: PEAP überträgt den Benutzernamen im Klartext und verschlüsselt nur das Passwort, während EAP-TTLS sowohl den Benutzernamen, als auch das Passwort über den TLS-Tunnel überträgt. Um EAP-PEAP zu konfigurieren, fügen Sie die folgenden Zeilen in [.filename]#/etc/wpa_supplicant.conf# ein: [.programlisting] .... network={ ssid="freebsdap" proto=RSN key_mgmt=WPA-EAP eap=PEAP <.> identity="test" <.> password="test" <.> ca_cert="/etc/certs/cacert.pem" <.> phase1="peaplabel=0" <.> phase2="auth=MSCHAPV2" <.> } .... <.> Die für die Verbindung verwendete EAP-Methode. <.> Das `identity`-Feld enthält den Identifizierungsstring für die innerhalb des verschlüsselten TLS-Tunnels erfolgende EAP-Authentifizierung. <.> Das Feld `password` enthält die Passphrase für die EAP-Authentifizierung. <.> Das Feld `ca_cert` gibt den Pfad zum CA-Zertifikat an. Diese Datei wird zur Verifizierung des Server-Zertifikats benötigt. -<.> Dieses Feld enthält die Parameter für die erste Phase der Authentifizierung, den TLS-Tunnel. Je nachdem, welcher Authentifizierungsserver benutzt wird, kann +<.> Dieses Feld enthält die Parameter für die erste Phase der Authentifizierung, den TLS-Tunnel. Je nachdem, welcher Authentifizierungsserver benutzt wird, kann ein spezifisches Label für die Authentifizierung verwendet werden. Meistens lautet das Label "client EAP encryption", dass durch `peaplabel=0` gesetzt wird. Weitere Informationen finden Sie in man:wpa_supplicant.conf[5]. <.> Das innerhalb des verschlüsselten TLS-Tunnels verwendete Authentifizierungsprotokoll. In unserem Beispiel handelt es sich dabei um `auth=MSCHAPV2`. Danach fügen Sie die folgende Zeile in [.filename]#/etc/rc.conf# ein: [.programlisting] .... ifconfig_ath0="WPA DHCP" .... Nun kann das drahtlose Gerät aktiviert werden. [source,shell] .... # service netif start Starting wpa_supplicant. DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 7 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 15 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 21 DHCPACK from 192.168.0.20 bound to 192.168.0.254 -- renewal in 300 seconds. wlan0: flags=8843 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.254 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet DS/11Mbps mode 11g status: associated ssid freebsdap channel 1 (2412 MHz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 3:128-bit txpower 21.5 bmiss 7 scanvalid 450 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst roaming MANUAL .... [[network-wireless-wep]] ===== WEP Wired Equivalent Privacy (WEP) ist Teil des ursprünglichen 802.11-Standards. Es enthält keinen Authentifzierungsmechanismus und verfügt lediglich über eine schwache Zugriffskontrolle, die sehr leicht umgangen werden kann. WEP kann über man:ifconfig[8] aktiviert werden: [source,shell] .... # ifconfig wlan0 create wlandev ath0 # ifconfig wlan0 inet 192.168.1.100 netmask 255.255.255.0 \ ssid my_net wepmode on weptxkey 3 wepkey 3:0x3456789012 .... * `weptxkey` definiert den WEP-Schlüssel, der für die Datenübertragung verwendet wird. Dieses Beispiel verwendet den dritten Schlüssel. Der gleiche Schlüssel muss auch am Access Point eingestellt sein. Kennen Sie den vom Access Point verwendeten Schlüssel nicht, sollten Sie zuerst den Wert `1` (den ersten Schlüssel) für diese Variable verwenden. * `wepkey` legt den zu verwendenden WEP-Schlüssel in der Form _Nummer:Schlüssel_ fest. Schlüssel `1` wird standardmäßig verwendet. Die "Nummer" muss nur angegeben werden, wenn ein anderer als der erste Schlüssel verwendet werden soll. + [NOTE] ==== Ersetzen Sie `0x3456789012` durch den am Access Point konfigurierten Schlüssel. ==== Weitere Informationen finden Sie in man:ifconfig[8]. Das Programm man:wpa_supplicant[8] eignet sich ebenfalls dazu, WEP für drahtlose Geräte zu aktivieren. Obige Konfiguration lässt sich dabei durch die Aufnahme der folgenden Zeilen in [.filename]#/etc/wpa_supplicant.conf# realisieren: [.programlisting] .... network={ ssid="my_net" key_mgmt=NONE wep_key3=3456789012 wep_tx_keyidx=3 } .... Danach müssen Sie das Programm noch aufrufen: [source,shell] .... # wpa_supplicant -i wlan0 -c /etc/wpa_supplicant.conf Trying to associate with 00:13:46:49:41:76 (SSID='dlinkap' freq=2437 MHz) Associated with 00:13:46:49:41:76 .... === Ad-hoc-Modus Der IBSS-Modus, der auch als Ad-hoc-Modus bezeichnet wird, ist für Punkt-zu-Punkt-Verbindungen vorgesehen. Um beispielsweise eine Ad-hoc-Verbindung zwischen den Rechnern `A` und `B` aufzubauen, werden lediglich zwei IP-Adressen und eine SSID benötigt. Auf Rechner `A`: [source,shell] .... # ifconfig wlan0 create wlandev ath0 wlanmode adhoc # ifconfig wlan0 inet 192.168.0.1 netmask 255.255.255.0 ssid freebsdap # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether 00:11:95:c3:0d:ac inet 192.168.0.1 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet autoselect mode 11g status: running ssid freebsdap channel 2 (2417 Mhz 11g) bssid 02:11:95:c3:0d:ac country US ecm authmode OPEN privacy OFF txpower 21.5 scanvalid 60 protmode CTS wme burst .... Der `adhoc`-Parameter zeigt an, dass die Schnittstelle im IBSS-Modus läuft. Rechner `B` sollte nun in der Lage sein, Rechner `A` zu finden: [source,shell] .... # ifconfig wlan0 create wlandev ath0 wlanmode adhoc # ifconfig wlan0 up scan SSID/MESH ID BSSID CHAN RATE S:N INT CAPS freebsdap 02:11:95:c3:0d:ac 2 54M -64:-96 100 IS WME .... Der Wert `I` (Spalte CAPS) in dieser Ausgabe bestätigt, dass sich Rechner `A` im Ad-hoc-Modus befindet. Nun müssen Sie noch Rechner `B` eine andere IP-Adresse zuweisen: [source,shell] .... # ifconfig wlan0 inet 192.168.0.2 netmask 255.255.255.0 ssid freebsdap # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.2 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet autoselect mode 11g status: running ssid freebsdap channel 2 (2417 Mhz 11g) bssid 02:11:95:c3:0d:ac country US ecm authmode OPEN privacy OFF txpower 21.5 scanvalid 60 protmode CTS wme burst .... Damit sind die Rechner `A` und `B` bereit und können untereinander Daten austauschen. [[network-wireless-ap]] === FreeBSD Host Access Points FreeBSD kann als Access Point (AP) agieren. Dies verhindert, dass man sich einen Hardware AP kaufen oder ein Ad-hoc Netzwerk laufen lassen muss. Dies kann sinnvoll sein, falls der FreeBSD-Computer als Gateway zu einem anderen Netzwerk, wie dem Internet, fungiert. [[network-wireless-ap-basic]] ==== Grundeinstellungen Bevor Sie einen FreeBSD-Computer als AP konfigurieren, muss der Kernel mit der entsprechenden Netzwerkunterstützung für die drahtlose Karte, sowie die Sicherheitsprotokolle konfiguriert werden. Weitere Informationen finden Sie im <>. [NOTE] ==== Die Verwendung der NDIS Treiber für Windows(R) erlauben zur Zeit keinen AP-Modus. Nur die nativen FreeBSD-Wireless-Treiber unterstützen den AP-Modus. ==== Nachdem die Netzwerkunterstützung geladen ist, überprüfen Sie, ob das Wireless-Gerät den hostbasierenden Access-Point Modus, der auch als hostap-Modus bekannt ist, unterstützt: [source,shell] .... # ifconfig wlan0 create wlandev ath0 # ifconfig wlan0 list caps drivercaps=6f85edc1 cryptocaps=1f .... Diese Ausgabe zeigt die Eigenschaften der Karte. Das Wort `HOSTAP` bestätigt, dass diese Wireless-Karte als AP agieren kann. Die verschiedenen unterstützten Algorithmen werden ebenfalls angezeigt: WEP, TKIP und AES. Diese Informationen zeigen an, welche Sicherheitsprotokolle auf dem AP nutzbar sind. Das Wireless-Gerät kann nur während der Erzeugung des Pseudo-Geräts in den hostap-Modus gesetzt werden. Zuvor erstellte Pseudo-Geräte müssen also vorher zerstört werden: [source,shell] .... # ifconfig wlan0 destroy .... Danach muss das Gerät erneut erstellt werden, bevor die restlichen Netzwerkparameter konfiguriert werden können: [source,shell] .... # ifconfig wlan0 create wlandev ath0 wlanmode hostap # ifconfig wlan0 inet 192.168.0.1 netmask 255.255.255.0 ssid freebsdap mode 11g channel 1 .... Benutzen Sie danach erneut man:ifconfig[8], um den Status der [.filename]#wlan0#-Schnittstelle abzufragen: [source,shell] .... # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether 00:11:95:c3:0d:ac inet 192.168.0.1 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet autoselect mode 11g status: running ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode OPEN privacy OFF txpower 21.5 scanvalid 60 protmode CTS wme burst dtimperiod 1 -dfs .... Die `hostap`-Parameter geben die Schnittstelle an, die im hostbasierenden Access Point Modus läuft. Die Konfiguration der Schnittstelle kann durch Hinzufügen der folgenden Zeilen in die Datei [.filename]#/etc/rc.conf# automatisch während des Bootvorganges erfolgen: [.programlisting] .... wlans_ath0="wlan0" create_args_wlan0="wlanmode hostap" ifconfig_wlan0="inet 192.168.0.1 netmask 255.255.255.0 ssid freebsdap mode 11g channel 1" .... ==== Hostbasierender Access Point ohne Authentifizierung oder Verschlüsselung Obwohl es nicht empfohlen wird, einen AP ohne jegliche Authentifizierung oder Verschlüsselung laufen zu lassen, ist es eine einfache Art zu testen, ob der AP funktioniert. Diese Konfiguration ist auch wichtig für die Fehlersuche bei Client-Problemen. Nachdem der AP konfiguriert wurde, ist es möglich von einem anderen drahtlosen Computer eine Suche nach dem AP zu starten: [source,shell] .... # ifconfig wlan0 create wlandev ath0 # ifconfig wlan0 up scan SSID/MESH ID BSSID CHAN RATE S:N INT CAPS freebsdap 00:11:95:c3:0d:ac 1 54M -66:-96 100 ES WME .... Der Client-Rechner hat den AP gefunden und kann nun eine Verbindung aufbauen: [source,shell] .... # ifconfig wlan0 inet 192.168.0.2 netmask 255.255.255.0 ssid freebsdap # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.2 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet OFDM/54Mbps mode 11g status: associated ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode OPEN privacy OFF txpower 21.5 bmiss 7 scanvalid 60 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst .... [[network-wireless-ap-wpa]] ==== WPA2-hostbasierter Access Point Dieser Abschnitt beschäftigt sich mit der Konfiguration eines FreeBSD Access Point mit dem WPA2-Sicherheitsprotokoll. Weitere Einzelheiten zu WPA und der Konfiguration von Clients mit WPA finden Sie im <>. Der man:hostapd[8]-Dienst wird genutzt, um die Client-Authentifizierung und das Schlüsselmanagement auf dem AP mit aktiviertem WPA2 zu nutzen. Die folgende Konfiguration wird auf dem FreeBSD-Computer ausgeführt, der als AP agiert. Nachdem der AP korrekt arbeitet, sollte man:hostapd[8] automatisch beim Booten durch folgende Zeile in [.filename]#/etc/rc.conf# aktiviert werden: [.programlisting] .... hostapd_enable="YES" .... Bevor Sie versuchen man:hostapd[8] zu konfigurieren, konfigurieren Sie zunächst die Grundeinstellungen, wie im <> beschrieben. ===== WPA2-PSK WPA2-PSK ist für kleine Netzwerke gedacht, in denen die Verwendung eines Authentifizierungs-Backend-Server nicht möglich oder nicht erwünscht ist. Die Konfiguration wird in [.filename]#/etc/hostapd.conf# durchgeführt: [.programlisting] .... interface=wlan0 <.> debug=1 <.> ctrl_interface=/var/run/hostapd <.> ctrl_interface_group=wheel <.> ssid=freebsdap <.> wpa=2 <.> wpa_passphrase=freebsdmall <.> wpa_key_mgmt=WPA-PSK <.> wpa_pairwise=CCMP <.> .... <.> Die Wireless-Schnittstelle, die für den Access Point verwendet wird an. <.> Der debuglevel von man:hostapd[8] während der Ausführung. Ein Wert von `1` ist der kleinste zulässige Wert. <.> Der Pfadname des Verzeichnisses, der von man:hostapd[8] genutzt wird, um die Domain-Socket-Dateien zu speichern, die für die Kommunikation mit externen Programmen, wie z.B. man:hostapd_cli[8], benutzt werden. In diesem Beispiel wird der Standardwert verwendet. <.> Die Gruppe die Zugriff auf die Schnittstellendateien hat. <.> Der Name des drahtlosen Netzwerks (SSID). <.> Aktiviert WPA und gibt an welches WPA-Authentifizierungprotokoll benötigt wird. Ein Wert von `2` konfiguriert den AP mit WPA2. Setzen Sie den Wert nur auf `1`, wenn Sie das veraltete WPA benötigen. <.> Das ASCII-Passwort für die WPA-Authentifizierung. <.> Das verwendete Schlüsselmanagement-Protokoll. Dieses Beispiel nutzt WPA-PSK. <.> Die zulässigen Verschlüsselungsverfahren des Access-Points. In diesem Beispiel wird nur CCMP (AES) akzeptiert. CCMP ist eine Alternative zu TKIP und sollte wenn möglich eingesetzt werden. TKIP sollte nur da eingesetzt werden, wo kein CCMP möglich ist. Als nächstes wird hostapd gestartet: [source,shell] .... # service hostapd forcestart .... [source,shell] .... # ifconfig wlan0 wlan0: flags=8943 metric 0 mtu 1500 ether 04:f0:21:16:8e:10 inet6 fe80::6f0:21ff:fe16:8e10%wlan0 prefixlen 64 scopeid 0x9 nd6 options=21 media: IEEE 802.11 Wireless Ethernet autoselect mode 11na status: running ssid No5ignal channel 36 (5180 MHz 11a ht/40+) bssid 04:f0:21:16:8e:10 country US ecm authmode WPA2/802.11i privacy MIXED deftxkey 2 AES-CCM 2:128-bit AES-CCM 3:128-bit txpower 17 mcastrate 6 mgmtrate 6 scanvalid 60 ampdulimit 64k ampdudensity 8 shortgi wme burst dtimperiod 1 -dfs groups: wlan .... Sobald der AP läuft, können sich die Clients mit ihm verbinden. Weitere Informationen finden Sie im <>. Es ist möglich zu sehen, welche Stationen mit dem AP verbunden sind. Geben Sie dazu `ifconfig _wlan0_ list sta` ein. ==== WEP-hostbasierter Access Point Es ist nicht empfehlenswert, einen AP mit WEP zu konfigurieren, da es keine Authentifikationsmechanismen gibt und WEP leicht zu knacken ist. Einige ältere drahtlose Karten unterstützen nur WEP als Sicherheitsprotokoll. Diese Karten können nur mit einem AP ohne Authentifikation oder Verschlüsselung genutzt werden. Das Wireless-Gerät kann nun in den hostap-Modus versetzt werden und mit der korrekten SSID und IP-Adresse konfiguriert werden: [source,shell] .... # ifconfig wlan0 create wlandev ath0 wlanmode hostap # ifconfig wlan0 inet 192.168.0.1 netmask 255.255.255.0 \ ssid freebsdap wepmode on weptxkey 3 wepkey 3:0x3456789012 mode 11g .... * Der `weptxkey` zeigt an, welcher WEP-Schlüssel bei der Übertragung benutzt wird. In diesem Beispiel wird der dritte Schlüssel benutzt, da die Nummerierung bei `1` beginnt. Dieser Parameter muss angegeben werden, damit die Daten verschlüsselt werden. * Der `wepkey` gibt den gewählten WEP-Schlüssel an. Er sollte im folgenden Format _index:key_ vorliegen. Wenn kein Index vorhanden ist, wird der Schlüssel `1` benutzt. Ansonsten muss der Index manuell festgelegt werden. Benutzen Sie man:ifconfig[8] um den Status der [.filename]#wlan0#-Schnittstelle erneut anzuzeigen: [source,shell] .... # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether 00:11:95:c3:0d:ac inet 192.168.0.1 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet autoselect mode 11g status: running ssid freebsdap channel 4 (2427 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode OPEN privacy ON deftxkey 3 wepkey 3:40-bit txpower 21.5 scanvalid 60 protmode CTS wme burst dtimperiod 1 -dfs .... Es ist möglich, von einem anderen drahtlosen Computer eine Suche nach dem AP zu starten: [source,shell] .... # ifconfig wlan0 create wlandev ath0 # ifconfig wlan0 up scan SSID BSSID CHAN RATE S:N INT CAPS freebsdap 00:11:95:c3:0d:ac 1 54M 22:1 100 EPS .... Der Client-Rechner hat den AP gefunden und kann nun eine Verbindung aufbauen. Weitere Informationen finden Sie im <>. === Benutzung von drahtgebundenen und drahtlosen Verbindungen Eine Verbindung per Kabel bietet eine bessere Leistung und eine höhere Zuverlässigkeit, während die Wireless-Verbindung eine höhere Flexibilität und Mobilität bietet. Benutzer von Laptops wollen normalerweise beides nutzen und zwischen beiden Verbindungen hin und her schalten. Unter FreeBSD ist es möglich zwei oder mehr Netzwerkschnittstellen in einem "failover"-Mode zu kombinieren. Diese Konfiguration nutzt die beste verfügbare Verbindung aus einer Gruppe von Netzwerkverbindungen. Sobald sich der Linkstatus ändert, wechselt das Betriebssystem automatisch auf eine andere Verbindung. Link-Aggregation und Failover werden im <> behandelt. Ein Beispiel für die Verwendung von kabelgebundenen und drahtlosen Verbindungen gibt es im <>. === Problembehandlung Dieser Abschnitt beschreibt eine Reihe von Maßnahmen zur Behebung von alltäglichen Problemen mit Drahtlosnetzwerken. * Wird der Access Point bei der Suche nicht gefunden, überprüfen Sie, dass die Konfiguration des drahtlosen Geräts nicht die Anzahl der Kanäle beschränkt. * Wenn sich das Gerät nicht mit dem Access Point verbinden kann, überprüfen Sie, ob die Konfiguration der Station auch der des Access Points entspricht. Dazu gehören auch die Authentifzierungsmethode und die Sicherheitsprotokolle. Halten Sie die Konfiguration so einfach wie möglich. Wenn Sie ein Sicherheitsprotokoll wie WPA oder WEP verwenden, können Sie testweise den Access Point auf _offene Authentifizierung_ und _keine Sicherheit_ einstellen. -+ ++ Für die Fehlersuche steht man:wpa_supplicant[8] zur Verfügung. Starten Sie das Programm manuell mit der Option `-dd` und durchsuchen Sie anschließend die Systemprotokolle nach eventuellen Fehlermeldungen. * Sobald sich das Gerät mit dem Access Point verbinden kann, prüfen Sie die Netzwerkkonfiguration mit einfachen Werkzeugen wie man:ping[8]. * Zusätzlich gibt es auch zahlreiche Low-Level-Debugging-Werkzeuge. Die Ausgabe von Debugging-Informationen des 802.11 Protocol Support Layers lassen sich mit dem Programm man:wlandebug[8] aktivieren. Um beispielsweise während der Suche nach Access Points und des Aufbaus von 802.11-Verbindungen (Handshake) auftretende Systemmeldungen auf die Konsole auszugeben, verwenden Sie den folgenden Befehl: + [source,shell] .... # wlandebug -i wlan0 +scan+auth+debug+assoc net.wlan.0.debug: 0 => 0xc80000 .... -+ ++ Der 802.11-Layer liefert umfangreiche Statistiken, die mit dem Werkzeug `wlanstats`, das sich in [.filename]#/usr/src/tools/tools/net80211# befindet, abgerufen werden können. Diese Statistiken sollten alle Fehler identifizieren, die im 802.11-Layer auftreten. Beachten Sie aber, dass einige Fehler bereits im darunterliegenden Gerätetreiber auftreten und daher in diesen Statistiken nicht enthalten sind. Wie Sie Probleme des Gerätetreibers identifizieren, entnehmen Sie bitte der Dokumentation des Gerätetreibers. Wenn die oben genannten Informationen nicht helfen das Problem zu klären, erstellen Sie einen Problembericht, der die Ausgabe der weiter oben genannten Werkzeuge beinhaltet. [[network-usb-tethering]] == USB Tethering Viele Mobiltelefone bieten die Möglichkeit, ihre Datenverbindung über USB (oft "Tethering" genannt) zu teilen. Diese Funktion verwendet entweder das RNDIS-, CDC- oder ein Apple(R) iPhone(R)/iPad(R)-Protokoll. * Android(TM)-Geräte benutzen in der Regel den man:urndis[4]-Treiber. * Apple(R)-Geräte benutzen den man:ipheth[4]-Treiber. * Ältere Geräte benutzen oft den man:cdce[4]-Treiber. Bevor Sie ein Gerät anschließen, laden Sie den entsprechenden Treiber in den Kernel: [source,shell] .... # kldload if_urndis # kldload if_cdce # kldload if_ipheth .... Sobald das Gerät angeschlossen ist, steht es unter ``ue``__0__ wie ein normales Netzwerkgerät zur Verfügung. Stellen Sie sicher, dass die Option "USB Tethering" auf dem Gerät aktiviert ist. Um diese Änderungen dauerhaft zu speichern und den Treiber beim Booten als Modul zu laden, müssen die entsprechenden Zeilen in [.filename]#/boot/loader.conf# konfiguriert werden: [.programlisting] .... if_urndis_load="YES" if_cdce_load="YES" if_ipteth_load="YES" .... [[network-bluetooth]] == Bluetooth Bluetooth ermöglicht die Bildung von persönlichen Netzwerken über drahtlose Verbindungen bei einer maximalen Reichweite von 10 Metern und operiert im unlizensierten 2,4-GHz-Band. Solche Netzwerke werden normalerweise spontan gebildet, wenn sich mobile Geräte, wie Mobiltelefone, Handhelds oder Notebooks miteinander verbinden. Im Gegensatz zu Wireless LAN ermöglicht Bluetooth auch höherwertige Dienste, wie FTP-ähnliche Dateiserver, Filepushing, Sprachübertragung, Emulation von seriellen Verbindungen und mehr. Dieses Kapitel beschreibt die Verwendung von USB-Bluetooth-Adaptern in FreeBSD. Weiterhin werden verschiedene Bluetooth-Protokolle und Programme vorgestellt. === Die Bluetooth-Unterstützung aktivieren Der Bluetooth-Stack von FreeBSD verwendet das man:netgraph[4]-Framework. Viele Bluetooth-USB-Adapter werden durch den man:ng_ubt[4]-Treiber unterstützt. Auf dem Chip BCM2033 von Broadcom basierende Bluetooth-Geräte werden von den Treibern man:ubtbcmfw[4] sowie man:ng_ubt[4] unterstützt. Die Bluetooth-PC-Card 3CRWB60-A von 3Com verwendet den man:ng_bt3c[4]-Treiber. Serielle sowie auf UART basierende Bluetooth-Geräte werden von man:sio[4], man:ng_h4[4] sowie man:hcseriald[8] unterstützt. Bevor ein Gerät angeschlossen wird, muss der entsprechende Treiber in den Kernel geladen werden. Hier verwendet das Gerät den man:ng_ubt[4]-Treiber: [source,shell] .... # kldload ng_ubt .... Ist das Bluetooth-Gerät beim Systemstart angeschlossen, kann das entsprechende Modul bei Booten geladen werden, indem der entsprechende Treiber in [.filename]#/boot/loader.conf# hinzugefügt wird: [.programlisting] .... ng_ubt_load="YES" .... Sobald der Treiber geladen ist, schließen Sie den USB-Adapter an. Eine Meldung ähnlich der folgenden wird auf der Konsole und in [.filename]#/var/log/messages# erscheinen: [source,shell] .... ubt0: vendor 0x0a12 product 0x0001, rev 1.10/5.25, addr 2 ubt0: Interface 0 endpoints: interrupt=0x81, bulk-in=0x82, bulk-out=0x2 ubt0: Interface 1 (alt.config 5) endpoints: isoc-in=0x83, isoc-out=0x3, wMaxPacketSize=49, nframes=6, buffer size=294 .... Verwenden Sie das Startskript zum Starten und Beenden des Bluetooth-Stacks. Es ist empfehlenswert, den Bluetooth-Stack zu beenden, bevor Sie den Adapter entfernen. Das Starten des Bluetooth-Stacks kann das Starten von man:hcsecd[8] erfordern. Wenn Sie den Bluetooth-Stack starten, erhalten Sie eine Meldung ähnlich der folgenden: [source,shell] .... # service bluetooth start ubt0 BD_ADDR: 00:02:72:00:d4:1a Features: 0xff 0xff 0xf 00 00 00 00 00 <3-Slot> <5-Slot> Max. ACL packet size: 192 bytes Number of ACL packets: 8 Max. SCO packet size: 64 bytes Number of SCO packets: 8 .... === Suche nach anderen Bluetooth-Geräten Das Host Controller Interface (HCI) bietet eine einheitliche Methode für den Zugriff auf Bluetooth-Basisband-Funktionen. In FreeBSD wird ein netgraph HCI-Knoten für jedes Bluetooth-Gerät erstellt. Weitere Einzelheiten finden Sie in man:ng_hci[4]. Eine der wichtigsten Aufgaben ist das Auffinden von sich in Reichweite befindenden Bluetooth-Geräten. Diese Funktion wird als _inquiry_ bezeichnet. Inquiry sowie andere mit HCI in Verbindung stehende Funktionen werden von man:hccontrol[8] zur Verfügung gestellt. Das folgende Beispiel zeigt, wie man herausfindet, welche Bluetooth-Geräte sich in Reichweite befinden. Eine solche Abfrage dauert nur wenige Sekunden. Beachten Sie, dass ein Gerät nur dann antwortet, wenn es sich im Modus _discoverable_ befindet. [source,shell] .... % hccontrol -n ubt0hci inquiry Inquiry result, num_responses=1 Inquiry result #0 BD_ADDR: 00:80:37:29:19:a4 Page Scan Rep. Mode: 0x1 Page Scan Period Mode: 00 Page Scan Mode: 00 Class: 52:02:04 Clock offset: 0x78ef Inquiry complete. Status: No error [00] .... `BD_ADDR` stellt, ähnlich der MAC-Adresse einer Netzwerkkarte, die eindeutige Adresse eines Bluetooth-Gerätes dar. Diese Adresse ist für die Kommunikation mit dem Gerät nötig. Es ist aber auch möglich, `BD_ADDR` einen Klartextnamen zuzuweisen. [.filename]#/etc/bluetooth/hosts# enthält Informationen über die bekannten Bluetooth-Rechner. Das folgende Beispiel zeigt, wie man den Klartextnamen eines entfernten Geräts in Erfahrung bringen kann: [source,shell] .... % hccontrol -n ubt0hci remote_name_request 00:80:37:29:19:a4 BD_ADDR: 00:80:37:29:19:a4 Name: Pav's T39 .... Wenn Sie ein entferntes Bluetooth-Gerät abfragen, wird dieses den Rechner unter dem Namen "your.host.name (ubt0)" finden. Dieser Name kann aber jederzeit geändert werden. Entfernten Geräten können Aliase in [.filename]#/etc/bluetooth/hosts# zugewiesen werden. Weitere Informationen zu [.filename]#/etc/bluetooth/hosts# finden Sie in man:bluetooth.hosts[5]. Bluetooth ermöglicht Punkt-zu-Punkt-Verbindungen an denen nur zwei Bluetooth-Geräte beteiligt sind, aber auch Punkt-zu-Multipunkt-Verbindungen, bei denen eine Verbindung von mehreren Bluetooth-Geräten gemeinsam genutzt wird. Das folgende Beispiel zeigt, wie man eine Verbindung zu einem entferntem Gerät aufbauen kann: [source,shell] .... % hccontrol -n ubt0hci create_connection BT_ADDR .... `create_connection` aktzeptiert `BT_ADDR` oder auch einen Alias aus [.filename]#/etc/bluetooth/hosts#. Das folgende Beispiel zeigt, wie man die aktiven Basisbandverbindungen des lokalen Gerätes anzeigen kann: [source,shell] .... % hccontrol -n ubt0hci read_connection_list Remote BD_ADDR Handle Type Mode Role Encrypt Pending Queue State 00:80:37:29:19:a4 41 ACL 0 MAST NONE 0 0 OPEN .... Ein _connection handle_ ist für die Beendigung einer Basisbandverbindung nützlich. Im Normalfall werden inaktive Verbindungen aber automatisch vom Bluetooth-Stack getrennt. [source,shell] .... # hccontrol -n ubt0hci disconnect 41 Connection handle: 41 Reason: Connection terminated by local host [0x16] .... Rufen Sie `hccontrol help` auf, wenn Sie eine komplette Liste aller verfügbaren HCI-Befehle benötigen. Die meisten dieser Befehle müssen nicht als `root` ausgeführt werden. === Erstmaliger Verbindungsaufbau zwischen zwei Bluetooth-Geräten (Pairing) In der Voreinstellung nutzt Bluetooth keine Authentifizierung, daher kann sich jedes Bluetoothgerät mit jedem anderen Gerät verbinden. Ein Bluetoothgerät, wie beispielsweise ein Mobiltelefon, kann jedoch für einen bestimmten Dienst, etwa eine Einwählverbindung, eine Authentifizierung anfordern. Bluetooth verwendet zu diesem Zweck _PIN-Codes_. Ein PIN-Code ist ein maximal 16 Zeichen langer ASCII-String. Damit eine Verbindung zustande kommt, muss auf beiden Geräten der gleiche PIN-Code verwendet werden. Nachdem der Code eingegeben wurde, erzeugen beide Geräte einen _link key_, der auf den Geräten gespeichert wird. Beim nächsten Verbindungsaufbau wird der zuvor erzeugte Link Key verwendet. Diesen Vorgang bezeichnet man als Pairing. Geht der Link Key auf einem Gerät verloren, muss das Pairing wiederholt werden. Der man:hcsecd[8]-Daemon verarbeitet Bluetooth-Authentifzierungsanforderungen und wird über die Datei [.filename]#/etc/bluetooth/hcsecd.conf# konfiguriert. Der folgende Ausschnitt dieser Datei zeigt die Konfiguration für ein Mobiltelefon, das den PIN-Code "1234" verwendet: [.programlisting] .... device { bdaddr 00:80:37:29:19:a4; name "Pav's T39"; key nokey; pin "1234"; } .... Von der Länge abgesehen, unterliegen PIN-Codes keinen Einschränkungen. Einige Geräte, beispielsweise Bluetooth-Headsets, haben einen festen PIN-Code eingebaut. Die Option `-d` sorgt dafür, dass der man:hcsecd[8]-Daemon im Vordergrund läuft. Dadurch kann der Ablauf einfach verfolgt werden. Stellen Sie das entfernte Gerät auf receive pairing und initiieren Sie die Bluetoothverbindung auf dem entfernten Gerät. Sie erhalten die Meldung, dass Pairing akzeptiert wurde und der PIN-Code benötigt wird. Geben Sie den gleichen PIN-Code ein, den Sie in [.filename]#hcsecd.conf# festgelegt haben. Der Computer und das entfernte Gerät sind nun miteinander verbunden. Alternativ können Sie das Pairing auch auf dem entfernten Gerät initiieren. man:hcsecd[8] kann durch das Einfügen der folgenden Zeile in [.filename]#/etc/rc.conf# beim Systemstart automatisch aktiviert werden: [.programlisting] .... hcsecd_enable="YES" .... Es folgt nun eine beispielhafte Ausgabe des man:hcsecd[8]-Daemons: [.programlisting] .... hcsecd[16484]: Got Link_Key_Request event from 'ubt0hci', remote bdaddr 0:80:37:29:19:a4 hcsecd[16484]: Found matching entry, remote bdaddr 0:80:37:29:19:a4, name 'Pav's T39', link key doesn't exist hcsecd[16484]: Sending Link_Key_Negative_Reply to 'ubt0hci' for remote bdaddr 0:80:37:29:19:a4 hcsecd[16484]: Got PIN_Code_Request event from 'ubt0hci', remote bdaddr 0:80:37:29:19:a4 hcsecd[16484]: Found matching entry, remote bdaddr 0:80:37:29:19:a4, name 'Pav's T39', PIN code exists hcsecd[16484]: Sending PIN_Code_Reply to 'ubt0hci' for remote bdaddr 0:80:37:29:19:a4 .... === Einwahlverbindungen und Netzwerkverbindungen mit PPP-Profilen einrichten Ein Dial-Up Networking-Profil (DUN) kann dazu benutzt werden, ein Mobiltelefon als drahtloses Modem zu nutzen, um sich über einen Einwahlprovider mit dem Internet zu verbinden. Es kann auch dazu genutzt werden, einen Computer so zu konfigurieren, dass dieser Datenabfragen empfängt. Der Zugriff auf ein Netzwerk über ein PPP-Profil kann einen Zugriff auf das LAN für ein oder mehrere Bluetooth-Geräte bieten. Eine PC-zu-PC-Verbindung unter Verwendung einer PPP-Verbindung über eine serielle Verbindung ist ebenfalls möglich. Diese Profile werden unter FreeBSD durch man:ppp[8] sowie man:rfcomm_pppd[8] implementiert - einem Wrapper, der Bluetooth-Verbindungen unter PPP nutzbar macht. Bevor ein Profil verwendet werden kann, muss ein neuer PPP-Abschnitt in [.filename]#/etc/ppp/ppp.conf# erzeugt werden. Beispielkonfigurationen zu diesem Thema finden Sie in man:rfcomm_pppd[8]. Dieses Beispiel verwendet man:rfcomm_pppd[8], um eine Verbindung zu einem entfernten Gerät mit der `BD_ADDR 00:80:37:29:19:a4` auf dem RFCOMM-Kanal `DUN` aufzubauen: [source,shell] .... # rfcomm_pppd -a 00:80:37:29:19:a4 -c -C dun -l rfcomm-dialup .... Die aktuelle Kanalnummer des entfernten Geräts erhalten Sie über das SDP-Protokoll. Es ist auch möglich, manuell einen RFCOMM-Kanal festzulegen. In diesem Fall führt man:rfcomm_pppd[8] keine SDP-Abfrage durch. Verwenden Sie man:sdpcontrol[8], um die RFCOMM-Kanäle des entfernten Geräts herauszufinden. Der man:sdpd[8]-Server muss laufen, damit ein Netzzugriff mit dem PPPLAN-Profil möglich ist. Außerdem muss für den LAN-Client ein neuer Eintrag in [.filename]#/etc/ppp/ppp.conf# erzeugt werden. Beispielkonfigurationen zu diesem Thema finden Sie in man:rfcomm_pppd[8]. Danach starten Sie den RFCOMMPPP-Server über eine gültige RFCOMM-Kanalnummer. Der RFCOMMPPP-Server bindet dadurch den Bluetooth-LAN-Dienst an den lokalen SDP-Daemon. Das folgende Beispiel zeigt, wie man den RFCOMMPPP-Server startet. [source,shell] .... # rfcomm_pppd -s -C 7 -l rfcomm-server .... === Bluetooth-Protokolle Dieser Abschnitt gibt einen Überblick über die verschiedenen Bluetooth-Protokolle, ihre Funktionen sowie weitere Programme. ==== Das Logical Link Control and Adaptation Protocol (L2CAP) Das Logical Link Control and Adaptation Protocol (L2CAP) bietet höherwertigen Protokollen verbindungsorientierte und verbindungslose Datendienste an. L2CAP erlaubt höherwertigen Protokollen und Programmen den Versand und Empfang von L2CAP-Datenpaketen mit einer Länge von bis zu 64 Kilobytes. L2CAP arbeitet _kanal_basiert. Ein Kanal ist eine logische Verbindung innerhalb einer Basisbandverbindung. Jeder Kanal ist dabei an ein einziges Protokoll gebunden. Mehrere Geräte können an das gleiche Protokoll gebunden sein, es ist aber nicht möglich, einen Kanal an mehrere Protokolle zu binden. Jedes über einen Kanal ankommende L2CAP-Paket wird an das entsprechende höherwertige Protokoll weitergeleitet. Mehrere Kanäle können sich die gleiche Basisbandverbindung teilen. Unter FreeBSD wird eine netgraph-Gerätedatei vom Typ _l2cap_ für jedes einzelne Bluetooth-Gerät erzeugt. Diese Gerätedatei ist normalerweise mit der Bluetooth-HCI-Gerätedatei (downstream) sowie der Bluetooth-Socket-Gerätedatei (upstream) verbunden. Der Standardname für die L2CAP-Gerätedatei lautet "devicel2cap". Weitere Details finden Sie in man:ng_l2cap[4]. Ein nützlicher Befehl zum Anpingen von anderen Geräten ist man:l2ping[8]. Einige Bluetooth-Geräte senden allerdings nicht alle erhaltenen Daten zurück. Die Ausgabe `0 bytes` im folgenden Beispiel ist also kein Fehler: [source,shell] .... # l2ping -a 00:80:37:29:19:a4 0 bytes from 0:80:37:29:19:a4 seq_no=0 time=48.633 ms result=0 0 bytes from 0:80:37:29:19:a4 seq_no=1 time=37.551 ms result=0 0 bytes from 0:80:37:29:19:a4 seq_no=2 time=28.324 ms result=0 0 bytes from 0:80:37:29:19:a4 seq_no=3 time=46.150 ms result=0 .... Das Programm man:l2control[8] liefert Informationen über L2CAP-Dateien. Das folgende Beispiel zeigt, wie man die Liste der logischen Verbindungen (Kanäle) sowie die Liste der Basisbandverbindungen abfragen kann: [source,shell] .... % l2control -a 00:02:72:00:d4:1a read_channel_list L2CAP channels: Remote BD_ADDR SCID/ DCID PSM IMTU/ OMTU State 00:07:e0:00:0b:ca 66/ 64 3 132/ 672 OPEN % l2control -a 00:02:72:00:d4:1a read_connection_list L2CAP connections: Remote BD_ADDR Handle Flags Pending State 00:07:e0:00:0b:ca 41 O 0 OPEN .... man:btsockstat[1] ist ein weiteres Diagnoseprogramm. Es funktioniert ähnlich wie man:netstat[1], arbeitet aber mit Bluetooth-Datenstrukturen. Das folgende Beispiel zeigt die gleiche Liste der logischen Verbindungen wie man:l2control[8] im vorherigen Beispiel. [source,shell] .... % btsockstat Active L2CAP sockets PCB Recv-Q Send-Q Local address/PSM Foreign address CID State c2afe900 0 0 00:02:72:00:d4:1a/3 00:07:e0:00:0b:ca 66 OPEN Active RFCOMM sessions L2PCB PCB Flag MTU Out-Q DLCs State c2afe900 c2b53380 1 127 0 Yes OPEN Active RFCOMM sockets PCB Recv-Q Send-Q Local address Foreign address Chan DLCI State c2e8bc80 0 250 00:02:72:00:d4:1a 00:07:e0:00:0b:ca 3 6 OPEN .... ==== Radio Frequency Communication (RFCOMM) Das RFCOMM-Protokoll emuliert serielle Verbindungen über das L2CAP-Protokoll. Bei RFCOMM handelt es sich um ein einfaches Transportprotokoll, das um Funktionen zur Emulation der 9poligen Schaltkreise von mit RS-232 (EIATIA-232-E) kompatiblen seriellen Ports ergänzt wurde. Es erlaubt bis zu 60 simultane Verbindungen (RFCOMM-Kanäle) zwischen zwei Bluetooth-Geräten. Eine RFCOMM-Kommunikation besteht aus zwei Anwendungen (den Kommunikationsendpunkten), die über das Kommunikationssegment miteinander verbunden sind. RFCOMM unterstützt Anwendungen, die auf serielle Ports angewiesen sind. Das Kommunikationssegment entspricht der direkten Bluetooth-Verbindung zwischen den beiden Geräten. RFCOMM kümmert sich um die direkte Verbindung von zwei Geräten, oder um die Verbindung zwischen einem Gerät und einem Modem über eine Netzwerkverbindung. RFCOMM unterstützt auch andere Konfigurationen. Ein Beispiel dafür sind Module, die drahtlose Bluetooth-Geräte mit einer verkabelten Schnittstelle verbinden können. Unter FreeBSD ist das RFCOMM-Protokoll im Bluetooth Socket-Layer implementiert. ==== Das Service Discovery Protocol (SDP) Das Service Discovery Protocol (SDP) erlaubt es Clientanwendungen, von Serveranwendungen angebotene Dienste sowie deren Eigenschaften abzufragen. Zu diesen Eigenschaften gehören die Art oder die Klasse der angebotenen Dienste sowie der Mechanismus oder das Protokoll, die zur Nutzung des Dienstes notwendig sind. SDP ermöglicht Verbindungen zwischen einem SDP-Server und einem SDP-Client. Der Server enthält eine Liste mit den Eigenschaften der vom Server angebotenen Dienste. Jeder Eintrag beschreibt jeweils einen einzigen Serverdienst. Ein Client kann diese Informationen durch eine SDP-Anforderung vom SDP-Server beziehen. Wenn der Client oder eine Anwendung des Clients einen Dienst nutzen will, muss eine separate Verbindung mit dem Dienstanbieter aufgebaut werden. SDP bietet einen Mechanismus zum Auffinden von Diensten und deren Eigenschaften an, es bietet aber keine Mechanismen zur Verwendung dieser Dienste. Normalerweise sucht ein SDP-Client nur nach Diensten, die bestimmte geforderte Eigenschaften erfüllen. Es ist aber auch möglich, anhand der Dienstbeschreibungen eine allgemeine Suche nach den von einem SDP-Server angebotenen Diensten durchzuführen. Diesen Vorgang bezeichnet man als Browsing. Der Bluetooth-SDP-Server man:sdpd[8] und der Kommandozeilenclient man:sdpcontrol[8] sind bereits in der Standardinstallation von FreeBSD enthalten. Das folgende Beispiel zeigt, wie eine SDP-Abfrage durchgeführt wird: [source,shell] .... % sdpcontrol -a 00:01:03:fc:6e:ec browse Record Handle: 00000000 Service Class ID List: Service Discovery Server (0x1000) Protocol Descriptor List: L2CAP (0x0100) Protocol specific parameter #1: u/int/uuid16 1 Protocol specific parameter #2: u/int/uuid16 1 Record Handle: 0x00000001 Service Class ID List: Browse Group Descriptor (0x1001) Record Handle: 0x00000002 Service Class ID List: LAN Access Using PPP (0x1102) Protocol Descriptor List: L2CAP (0x0100) RFCOMM (0x0003) Protocol specific parameter #1: u/int8/bool 1 Bluetooth Profile Descriptor List: LAN Access Using PPP (0x1102) ver. 1.0 .... Beachten Sie, dass jeder Dienst eine Liste seiner Eigenschaften, wie etwa den RFCOMM-Kanal, zurückgibt. Je nachdem, welche Dienste der Benutzer benötigt, sollten einige dieser Eigenschaften notiert werden. Einige Bluetooth-Implementationen unterstützen kein Service Browsing und geben daher eine leere Liste zurück. Ist dies der Fall, ist es dennoch möglich, nach einem bestimmten Dienst zu suchen. Das folgende Beispiel demonstriert die Suche nach dem OBEX Object Push (OPUSH) Dienst: [source,shell] .... % sdpcontrol -a 00:01:03:fc:6e:ec search OPUSH .... Unter FreeBSD ist es die Aufgabe des man:sdpd[8]-Servers, Bluetooth-Clients verschiedene Dienste anzubieten. Sie können diesen Server durch das Einfügen der folgenden Zeile in [.filename]#/etc/rc.conf# aktivieren: [.programlisting] .... sdpd_enable="YES" .... Nun kann der man:sdpd[8]-Daemon durch folgende Eingabe gestartet werden: [source,shell] .... # service sdpd start .... Der lokale Server, der den entfernten Clients Bluetooth-Dienste anbieten soll, bindet diese Dienste an den lokalen SDP-Daemon. Ein Beispiel für eine solche Anwendung ist man:rfcomm_pppd[8]. Einmal gestartet, wird der Bluetooth-LAN-Dienst an den lokalen SDP-Daemon gebunden. Die Liste der vorhandenen Dienste, die am lokalen SDP-Server registriert sind, lässt sich durch eine SDP-Abfrage über einen lokalen Kontrollkanal abfragen: [source,shell] .... # sdpcontrol -l browse .... ==== OBEX Object-Push (OPUSH) OBEX ist ein häufig verwendetes Protokoll für den Dateitransfer zwischen Mobilgeräten. Sein Hauptzweck ist die Kommunikation über die Infrarotschnittstelle. Es dient daher zum Datentransfer zwischen Notebooks oder PDAs sowie zum Austausch von Visitenkarten oder Kalendereinträgen zwischen Mobiltelefonen und anderen Geräten mit PIM-Funktionen. Server und Client von OBEX werden durch obexapp bereitgestellt, das als Paket oder Port package:comms/obexapp[] installiert werden kann. Mit dem OBEX-Client werden Objekte zum OBEX-Server geschickt oder angefordert. Ein Objekt kann etwa eine Visitenkarte oder ein Termin sein. Der OBEX-Client fordert über SDP die Nummer des RFCOMM-Kanals vom entfernten Gerät an. Dies kann auch durch die Verwendung des Servicenamens anstelle der RFCOMM-Kanalnummer erfolgen. Folgende Dienste werden unterstützt: `IrMC`, `FTRN` und `OPUSH`. Es ist möglich, den RFCOMM-Kanal als Nummer anzugeben. Es folgt ein Beispiel für eine OBEX-Sitzung, bei der ein Informationsobjekt vom Mobiltelefon angefordert und ein neues Objekt (hier eine Visitenkarte) an das Telefonbuch des Mobiltelefons geschickt wird: [source,shell] .... % obexapp -a 00:80:37:29:19:a4 -C IrMC obex> get telecom/devinfo.txt Success, response: OK, Success (0x20) obex> put new.vcf Success, response: OK, Success (0x20) obex> di Success, response: OK, Success (0x20) .... Um OBEX-Push-Dienste anbieten zu können, muss der sdpd-Server gestartet sein. Ein Wurzelverzeichnis, in dem alle ankommenden Objekte gespeichert werden, muss zusätzlich angelegt werden. In der Voreinstellung ist dies [.filename]#/var/spool/obex#. Starten Sie den OBEX-Server mit einer gültigen Kanalnummer. Der OBEX-Server registriert nun den OBEX-Push-Dienst mit dem lokalen SDP-Daemon. Das folgende Beispiel zeigt, wie der OBEX-Server gestartet wird: [source,shell] .... # obexapp -s -C 10 .... ==== Das Serial-Port Profil (SPP) Das Serial Port Profile (SSP) ermöglicht es Bluetooth-Geräten eine serielle Kabelverbindung zu emulieren. Anwendungen sind dadurch in der Lage, über eine virtuelle serielle Verbindung Bluetooth als Ersatz für eine Kabelverbindung zu nutzen. man:rfcomm_sppd[1] implementiert unter FreeBSD SSP und ein Pseudo-tty, das als virtuelle serielle Verbindung verwendet wird. Das folgende Beispiel zeigt, wie man eine Verbindung mit einem entfernten Serial-Port-Dienst herstellt. Ein RFCOMM-Kanal muss dabei nicht angegeben werden, da man:rfcomm_sppd[1] den Kanal über SDP abfragen kann. Um dies zu umgehen, geben Sie einen RFCOMM-Kanal auf der Kommandozeile an. [source,shell] .... # rfcomm_sppd -a 00:07:E0:00:0B:CA -t rfcomm_sppd[94692]: Starting on /dev/pts/6... /dev/pts/6 .... Sobald die Verbindung hergestellt ist, kann pseudo-tty als serieller Port verwenden werden. [source,shell] .... # cu -l /dev/pts/6 .... Das pseudo-tty wird auf der Standardausgabe ausgegeben und kann von Wrapper-Skripten gelesen werden: [.programlisting] .... PTS=`rfcomm_sppd -a 00:07:E0:00:0B:CA -t` cu -l $PTS .... === Problembehandlung Wenn FreeBSD eine neue Verbindung akzeptiert, versucht es, die Rolle zu tauschen, um zum Master zu werden. Einige ältere Geräte, die dies nicht unterstützen, können keine Verbindung aufbauen. Da der Rollentausch ausgeführt wird sobald eine neue Verbindung aufgebaut wird, ist es nicht möglich, das entfernte Gerät zu fragen ob es den Rollentausch unterstützt. Es gibt jedoch eine HCI-Option, die dieses Verhalten deaktiviert: [source,shell] .... # hccontrol -n ubt0hci write_node_role_switch 0 .... Verwenden Sie hcidump, das als Paket Port package:comms/hcidump[] installiert werden kann, um Bluetooth-Pakete anzuzeigen. Dieses Programm hat Ähnlichkeiten mit man:tcpdump[1] und kann zur Anzeige der Bluetooth-Pakete in einem Terminal, oder zur Speicherung von Paketen in einer Datei (Dump) verwendet werden. [[network-bridging]] == LAN-Kopplung mit einer Bridge Manchmal ist es nützlich, ein Netzwerk, wie ein Ethernetsegment, in separate Netzwerke aufzuteilen, ohne gleich IP-Subnetze zu erzeugen, die über einen Router miteinander verbunden sind. Ein Gerät, das zwei Netze auf diese Weise verbindet, wird als "Bridge" bezeichnet. Eine Bridge arbeitet, indem sie die MAC-Adressen der Geräte in ihren Netzwerksegmenten lernt. Der Verkehr wird nur dann zwischen zwei Segmenten weitergeleitet, wenn sich Sender und Empfänger in verschiedenen Netzwerksegmenten befinden. Jedes FreeBSD-System mit zwei Netzwerkkarten kann als Bridge fungieren. Bridging kann in den folgenden Situationen sinnvoll sein: Verbinden von Netzwerken:: Die Hauptaufgabe einer Bridge ist die Verbindung von zwei oder mehreren Netzwerksegmenten. Es gibt viele Gründe, eine hostbasierte Bridge einzusetzen, anstelle von Netzwerkkomponenten, wie beispielsweise Kabelverbindungen oder Firewalls. Eine Bridge kann außerdem ein drahtloses Gerät mit einem Kabelnetzwerk verbinden. Diese Fähigkeit der Bridge wird als HostAP-Modus bezeichnet. Die Bridge agiert in diesem Fall als Access Point für das drahtlose Gerät. Filtering / Traffic Shaping Firewall:: Eine Bridge kann eingesetzt werden, wenn Firewallfunktionen benötigt werden, ohne dabei Routing oder Network Adress Translation (NAT) zu verwenden. + Ein Beispiel dafür wäre ein kleines Unternehmen, das über DSL oder ISDN an einen ISP angebunden ist. Es verfügt über 13 erreichbare IP-Adressen und das Netzwerk besteht aus 10 Rechnern. In dieser Situation ist der Einsatz von Subnetzen sowie einer routerbasierten Firewall aufgrund der IP-Adressierung schwierig. Eine Bridge-basierte Firewall kann hingegen ohne Probleme konfiguriert werden. Netzwerküberwachung:: Eine Bridge kann zwei Netzwerksegmente miteinander verbinden und danach alle Ethernet-Rahmen überprüfen, die zwischen den beiden Netzwerksegmenten ausgetauscht werden. Dazu verwendet man entweder man:bpf[4] und man:tcpdump[1] auf dem Netzgerät der Bridge oder schickt Kopien aller Rahmen an ein zusätzliches Netzgerät, das als Span Port bekannt ist. Layer 2 VPN:: Zwei Ethernetnetzwerke können über einen IP-Link miteinander verbunden werden, indem die beiden Netzwerke über einen EtherIP-Tunnel gekoppelt werden, oder eine man:tap[4]-basierte Lösung wie OpenVPN eingesetzt wird. Layer 2 Redundanz:: Die Systeme eines Netzwerks können über das Spanning Tree Protocol (STP) redundant miteinander verbunden sein, um redundante Pfade zu blockieren. Dieser Abschnitt beschreibt, wie ein FreeBSD-System mit Hilfe von man:if_bridge[4] als Bridge konfiguriert wird. Ein netgraph-Bridge-Treiber ist ebenfalls verfügbar und wird in man:ng_bridge[4] beschrieben. [NOTE] ==== Paketfilter können mit allen Firewallpaketen verwendet werden, die das man:pfil[9]-Framework benutzen. Eine Bridge kann auch als Traffic Shaper verwendet werden, wenn Sie man:altq[4] oder man:dummynet[4] einsetzen. ==== === Die Bridge aktivieren In FreeBSD handelt es sich bei man:if_bridge[4] um ein Kernelmodul, das von man:ifconfig[8] automatisch geladen wird, wenn eine Bridge-Schnittstelle erzeugt wird. Es ist auch möglich, die Unterstützung für den Treiber in den Kernel zu kompilieren, indem die Zeile `device if_bridge` in die Kernelkonfigurationsdatei hinzugefügt wird. Eine Bridge wird durch das Klonen von Schnittstellen erzeugt. Um eine Bridge zu erzeugen, verwenden Sie: [source,shell] .... # ifconfig bridge create bridge0 # ifconfig bridge0 bridge0: flags=8802 metric 0 mtu 1500 ether 96:3d:4b:f1:79:7a id 00:00:00:00:00:00 priority 32768 hellotime 2 fwddelay 15 maxage 20 holdcnt 6 proto rstp maxaddr 100 timeout 1200 root id 00:00:00:00:00:00 priority 0 ifcost 0 port 0 .... Wenn eine Bridge erzeugt wird, erhält sie automatisch eine zufällig generierte Ethernet-Adresse. Die Parameter `maxaddr` sowie `timeout` legen fest, wie viele MAC-Adressen die Bridge in ihrer Forward-Tabelle halten kann und wie viele Sekunden jeder Eintrag erhalten bleiben soll, nachdem er zuletzt verwendet wurde. Die restlichen Parameter sind für die Konfiguration von STP notwendig. Im nächsten Schritt werden die Schnittstellen, die die Bridge verbinden soll, zugewiesen. Damit die Bridge Datenpakete weiterleiten kann, müssen sowohl die Bridge als auch die Schnittstellen der zu verbindenden Netzwerksegmente aktiviert sein: [source,shell] .... # ifconfig bridge0 addm fxp0 addm fxp1 up # ifconfig fxp0 up # ifconfig fxp1 up .... Jetzt ist die Bridge in der Lage, Ethernet-Rahmen zwischen den Schnittstellen [.filename]#fxp0# und [.filename]#fxp1# weiterzuleiten. Um diese Konfiguration beim Systemstart automatisch zu aktivieren, müssen die folgenden Zeilen in [.filename]#/etc/rc.conf# hinzugefügt werden: [.programlisting] .... cloned_interfaces="bridge0" ifconfig_bridge0="addm fxp0 addm fxp1 up" ifconfig_fxp0="up" ifconfig_fxp1="up" .... Wenn die Bridge eine IP-Adresse benötigt, muss diese der Schnittstelle der Bridge zugewiesen werden und nicht der Schnittstelle der gekoppelten Netzwerksegmente. Die IP-Adresse kann manuell gesetzt, oder über DHCP bezogen werden. Dieses Beispiel verwendet eine statische IP-Adresse: [source,shell] .... # ifconfig bridge0 inet 192.168.0.1/24 .... Es ist auch möglich der Bridge-Schnittstelle eine IPv6-Adresse zuzuweisen. Um die Änderungen dauerhaft zu speichern, fügen Sie die Adressinformationen in [.filename]#/etc/rc.conf# ein. [NOTE] ==== Nachdem ein Paketfilter aktiviert wurde, können Datenpakete, die von den Schnittstellen der gekoppelten Netzwerksegmente gesendet und empfangen werden, über die Bridge weitergeleitet oder nach bestimmten Regeln gefiltert oder auch komplett geblockt werden. Ist die Richtung des Paketflusses wichtig, ist es am besten, eine Firewall auf den Schnittstellen der einzelnen Netzwerksegmente einzurichten und nicht auf der Bridge selbst. Eine Bridge verfügt über verschiedene Optionen zur Weiterleitung von Nicht-IP- und IP-Paketen, sowie Paketfilterung auf Layer 2 mittels man:ipfw[8]. Weitere Informationen finden Sie in man:if_bridge[4]. ==== === Spanning Tree aktivieren Damit ein Ethernet-Netzwerk richtig funktioniert, kann nur ein aktiver Pfad zwischen zwei Geräten existieren. Das STP-Protokoll erkennt Schleifen in einer Netzwerktopologie und setzt redundante Pfade in einen blockierten Zustand. Sollte eine der aktiven Verbindungen ausfallen, berechnet STP einen anderen Baum und ermöglicht es dann einem blockierten Pfad, alle Netzwerkverbindungen wiederherzustellen. Das Rapid Spanning Tree Protocol (RSTP oder 802.1w), ist abwärtskompatibel zum veralteten STP. RSTP arbeitet schneller und tauscht Informationen mit benachbarten Switchen aus, um Pakete korrekt weiterzuleiten und eine Schleifenbildung zu verhindern. FreeBSD unterstützt die Betriebsmodi RSTP und STP, wobei RSTP als Standardmodus voreingestellt ist. STP kann auf den Schnittstellen der durch die Bridge verbundenen Netzwerksegmente mittels man:ifconfig[8] aktiviert werden. Für eine Bridge, die die Schnittstellen [.filename]#fxp0# und [.filename]#fxp1# verbindet, aktivieren Sie STP wie folgt: [source,shell] .... # ifconfig bridge0 stp fxp0 stp fxp1 bridge0: flags=8843 metric 0 mtu 1500 ether d6:cf:d5:a0:94:6d id 00:01:02:4b:d4:50 priority 32768 hellotime 2 fwddelay 15 maxage 20 holdcnt 6 proto rstp maxaddr 100 timeout 1200 root id 00:01:02:4b:d4:50 priority 32768 ifcost 0 port 0 member: fxp0 flags=1c7 port 3 priority 128 path cost 200000 proto rstp role designated state forwarding member: fxp1 flags=1c7 port 4 priority 128 path cost 200000 proto rstp role designated state forwarding .... Diese Bridge hat die Spanning-Tree-ID `00:01:02:4b:d4:50` und die Priorität `32768`. Da diese ID mit der `Root-ID` identisch ist, handelt es sich um die Root-Bridge dieses Netzwerks. Auf einer anderen Bridge des Netzwerks ist STP ebenfalls aktiviert: [source,shell] .... bridge0: flags=8843 metric 0 mtu 1500 ether 96:3d:4b:f1:79:7a id 00:13:d4:9a:06:7a priority 32768 hellotime 2 fwddelay 15 maxage 20 holdcnt 6 proto rstp maxaddr 100 timeout 1200 root id 00:01:02:4b:d4:50 priority 32768 ifcost 400000 port 4 member: fxp0 flags=1c7 port 4 priority 128 path cost 200000 proto rstp role root state forwarding member: fxp1 flags=1c7 port 5 priority 128 path cost 200000 proto rstp role designated state forwarding .... Die Zeile `root id 00:01:02:4b:d4:50 priority 32768 ifcost 400000 port 4` zeigt an, dass die Root-Bridge die ID `00:01:02:4b:d4:50` hat. Die Pfadkosten hin zur Root-Bridge betragen `400000`, wobei der Pfad zur Root-Bridge über `port 4` geht, der wiederum der Schnittstelle [.filename]#fxp0# entspricht. === Parameter der Bridge-Schnittstelle Einige Parameter von `ifconfig` dienen ausschließlich der Konfiguration von Bridge-Schnittstellen. Dieser Abschnitt fasst die Verwendung dieser Parameter zusammen. Die vollständige Liste der verfügbaren Parameter wird in man:ifconfig[8] beschrieben. private:: Eine private Schnittstelle leitet keine Daten an einen Port weiter, bei dem es sich ebenfalls um eine private Schnittstelle handelt. Der Datenverkehr wird dabei komplett blockiert, auch Ethernet-Rahmen und ARP-Pakete werden nicht weitergeleitet. Wollen Sie hingegen nur spezifische Datenpakete blockieren, sollten Sie eine Firewall einsetzen. span:: Ein Span Port übertragt eine Kopie jedes Ethernet-Rahmens, der an der Bridge ankommt. Auf einer Bridge können beliebig viele Span Ports festgelegt werden. Wird eine Schnittstelle als Span Port konfiguriert, kann sie nicht mehr als normaler Bridge-Port verwendet werden. Eine derartige Konfiguration ist beispielsweise sinnvoll, um den Datenverkehr, der in einem Netzwerk über die Bridge läuft, auf einen Rechner zu übertragen, der mit einem Span Port der Bridge verbunden ist. Um beispielsweise eine Kopie aller Ethernet-Rahmen über die Schnittstelle [.filename]#fxp0# zu übertragen: + [source,shell] .... # ifconfig bridge0 span fxp4 .... sticky:: Wenn die Schnittstelle eines über eine Bridge verbundenen Netzwerksegments als sticky gekennzeichnet wird, werden alle dynamisch gelernten Adressen als statische Adressen behandelt, sobald sie in den Forward-Cache der Bridge aufgenommen wurden. Sticky-Einträge werden niemals aus dem Cache entfernt oder ersetzt. Selbst dann nicht, wenn die Adresse von einer anderen Schnittstelle verwendet wird. Sie können dadurch die Vorteile statischer Adresseinträge nutzen, ohne die Forward-Tabelle vor dem Einsatz der Bridge mit statischen Einträgen füllen zu müssen. Clients, die sich in einem bestimmten von der Bridge verwalteten Segmente befinden, können dabei nicht in ein anderes Segment wechseln. + Ein Beispiel für den Einsatz von Sticky-Adressen ist die Kombination einer Bridge mit mehreren VLANs, um einen Router zu konfigurieren, der einzelne Kundennetzwerke voneinander trennt, ohne dabei IP-Adressbereiche zu verschwenden. Für das folgende Beispiel nehmen wir an, dass sich der Client `CustomerA` im VLAN `vlan100` und der Client `CustomerB` im VLAN `vlan101` befinden. Die Bridge hat die IP-Adresse `192.168.0.1`: + [source,shell] .... # ifconfig bridge0 addm vlan100 sticky vlan100 addm vlan101 sticky vlan101 # ifconfig bridge0 inet 192.168.0.1/24 .... + In diesem Beispiel sehen beide Clients `192.168.0.1` als das Default-Gateway. Da der Brücken-Cache _sticky_ ist, sind Sie nicht dazu in der Lage, die MAC-Adresse des anderen Kunden zu spoofen und dessen Datenverkehr abzufangen. + Sie können die Kommunikation zwischen den VLANs vollständig unterbinden, wenn Sie private Schnittstellen oder eine Firewall einsetzen: + [source,shell] .... # ifconfig bridge0 private vlan100 private vlan101 .... + Die Kunden sind nun komplett voneinander isoliert und der komplette `/24`-Adressbereich kann zugewiesen werden, ohne dass Subnetze eingesetzt werden. + Die maximale mögliche Anzahl an eindeutigen MAC-Adressen hinter einer Schnittstelle kann festgelegt werden. Sobald das Limit erreicht ist, werden Pakete mit einer unbekannten Quell-Adresse solange verworfen, bis ein existierender Eintrag gelöscht wird oder abläuft. + Das folgende Beispiel setzt die maximale Anzahl von Netzgeräten für `CustomerA` für das VLAN `vlan100` auf 10. + [source,shell] .... # ifconfig bridge0 ifmaxaddr vlan100 10 .... Die Bridge unterstützt auch den Monitormodus. Dabei werden alle Pakete verworfen, nachdem sie von man:bpf[4] verarbeitet wurden. In diesem Modus erfolgt keine weitere Bearbeitung und auch keine Weiterleitung von Datenpaketen. Es ist daher möglich, die Eingabe von zwei oder mehr Netzwerkschnittstellen in einen einzigen gemeinsamen man:bpf[4]-Stream zu vereinen. Ein solcher Datenstrom ist beispielsweise nützlich, um den Datenverkehr für "network taps" zu rekonstruieren, die ihre RX/TX-Signale über verschiedene Schnittstellen senden. Um beispielsweise die Eingabe von vier Netzwerkschnittstellen in einzigen gemeinsamen Datenstrom zu vereinen: [source,shell] .... # ifconfig bridge0 addm fxp0 addm fxp1 addm fxp2 addm fxp3 monitor up # tcpdump -i bridge0 .... === SNMP-Monitoring Die Schnittstelle der Bridge sowie die STP-Parameter können durch den im Basissystem enthaltenen man:bsnmpd[1] überwacht werden. Die exportierten Bridge-MIBs entsprechen den IETF-Standards, daher kann ein beliebiger SNMP-Client oder ein beliebiges Monitoring-Werkzeug eingesetzt werden, um die benötigten Daten zu erhalten. Um das Monitoring auf der Bridge zu aktivieren, kommentieren Sie diese Zeile in [.filename]#/etc/snmpd.config# aus, indem Sie das Zeichen `#` entfernen: [.programlisting] .... begemotSnmpdModulePath."bridge" = "/usr/lib/snmp_bridge.so" .... Weitere Konfigurationsparameter wie Community-Namen und Zugriffslisten müssen ebenfalls in dieser Datei angepasst werden. Weitere Informationen finden Sie in man:bsnmpd[1] und man:snmp_bridge[3]. Nachdem die Änderungen gespeichert wurden, fügen Sie folgende Zeile in [.filename]#/etc/rc.conf# hinzu: [.programlisting] .... bsnmpd_enable="YES" .... Danach starten Sie man:bsnmpd[1]: [source,shell] .... # service bsnmpd start .... Die folgenden Beispiele verwenden das Softwarepaket Net-SNMP (package:net-mgmt/net-snmp[]), um die Bridge vom Client aus abzufragen. Alternativ kann auch der Port package:net-mgmt/bsnmptools[] benutzt werden. Auf dem SNMP-Client müssen danach die folgenden Zeilen in [.filename]#$HOME/.snmp/snmp.conf# hinzugefügt werden, um die MIB-Definitionen der Bridge in Net-SNMP zu importieren: [.programlisting] .... mibdirs +/usr/shared/snmp/mibs mibs +BRIDGE-MIB:RSTP-MIB:BEGEMOT-MIB:BEGEMOT-BRIDGE-MIB .... Um eine einzelne Bridge über den IETF BRIDGE-MIB (RFC4188) zu überwachen: [source,shell] .... % snmpwalk -v 2c -c public bridge1.example.com mib-2.dot1dBridge BRIDGE-MIB::dot1dBaseBridgeAddress.0 = STRING: 66:fb:9b:6e:5c:44 BRIDGE-MIB::dot1dBaseNumPorts.0 = INTEGER: 1 ports BRIDGE-MIB::dot1dStpTimeSinceTopologyChange.0 = Timeticks: (189959) 0:31:39.59 centi-seconds BRIDGE-MIB::dot1dStpTopChanges.0 = Counter32: 2 BRIDGE-MIB::dot1dStpDesignatedRoot.0 = Hex-STRING: 80 00 00 01 02 4B D4 50 ... BRIDGE-MIB::dot1dStpPortState.3 = INTEGER: forwarding(5) BRIDGE-MIB::dot1dStpPortEnable.3 = INTEGER: enabled(1) BRIDGE-MIB::dot1dStpPortPathCost.3 = INTEGER: 200000 BRIDGE-MIB::dot1dStpPortDesignatedRoot.3 = Hex-STRING: 80 00 00 01 02 4B D4 50 BRIDGE-MIB::dot1dStpPortDesignatedCost.3 = INTEGER: 0 BRIDGE-MIB::dot1dStpPortDesignatedBridge.3 = Hex-STRING: 80 00 00 01 02 4B D4 50 BRIDGE-MIB::dot1dStpPortDesignatedPort.3 = Hex-STRING: 03 80 BRIDGE-MIB::dot1dStpPortForwardTransitions.3 = Counter32: 1 RSTP-MIB::dot1dStpVersion.0 = INTEGER: rstp(2) .... Der Wert der Variable `dot1dStpTopChanges.0` ist hier 2, die STP-Topologie der Bridge wurde also bereits zweimal geändert. Unter einer Änderung versteht man die Anpassung eines oder mehrerer Links und die Kalkulation eines neuen Baums. Der Wert der Variable `dot1dStpTimeSinceTopologyChange.0` gibt an, wann dies zuletzt geschah. Um mehrere Bridge-Schnittstellen zu überwachen, kann der private BEGEMOT-BRIDGE-MIB eingesetzt werden: [source,shell] .... % snmpwalk -v 2c -c public bridge1.example.com enterprises.fokus.begemot.begemotBridge BEGEMOT-BRIDGE-MIB::begemotBridgeBaseName."bridge0" = STRING: bridge0 BEGEMOT-BRIDGE-MIB::begemotBridgeBaseName."bridge2" = STRING: bridge2 BEGEMOT-BRIDGE-MIB::begemotBridgeBaseAddress."bridge0" = STRING: e:ce:3b:5a:9e:13 BEGEMOT-BRIDGE-MIB::begemotBridgeBaseAddress."bridge2" = STRING: 12:5e:4d:74:d:fc BEGEMOT-BRIDGE-MIB::begemotBridgeBaseNumPorts."bridge0" = INTEGER: 1 BEGEMOT-BRIDGE-MIB::begemotBridgeBaseNumPorts."bridge2" = INTEGER: 1 ... BEGEMOT-BRIDGE-MIB::begemotBridgeStpTimeSinceTopologyChange."bridge0" = Timeticks: (116927) 0:19:29.27 centi-seconds BEGEMOT-BRIDGE-MIB::begemotBridgeStpTimeSinceTopologyChange."bridge2" = Timeticks: (82773) 0:13:47.73 centi-seconds BEGEMOT-BRIDGE-MIB::begemotBridgeStpTopChanges."bridge0" = Counter32: 1 BEGEMOT-BRIDGE-MIB::begemotBridgeStpTopChanges."bridge2" = Counter32: 1 BEGEMOT-BRIDGE-MIB::begemotBridgeStpDesignatedRoot."bridge0" = Hex-STRING: 80 00 00 40 95 30 5E 31 BEGEMOT-BRIDGE-MIB::begemotBridgeStpDesignatedRoot."bridge2" = Hex-STRING: 80 00 00 50 8B B8 C6 A9 .... Um die über den `mib-2.dot1dBridge`-Subtree überwachte Bridge-Schnittstelle zu ändern: [source,shell] .... % snmpset -v 2c -c private bridge1.example.com BEGEMOT-BRIDGE-MIB::begemotBridgeDefaultBridgeIf.0 s bridge2 .... [[network-aggregation]] == Link-Aggregation und Failover Die von FreeBSD unterstützte man:lagg[4]-Schnittstelle erlaubt die Gruppierung von mehreren Netzwerkadaptern als eine virtuelle Schnittstelle, mit dem Ziel, Ausfallsicherheit (Failover) und Link Aggregation bereitzustellen. Bei Failover kann der Verkehr auch dann weiter fließen, wenn nur eine Schnittstelle verfügbar ist. Link Aggregation funktioniert am besten mit Switches, die LCAP unterstützen, da dieses Protokoll den Datenverkehr bidirektional verteilt, während es auch auf den Ausfall einzelner Verbindungen reagiert. Die von der lagg-Schnittstelle unterstützten Protokolle bestimmen, welche Ports für den ausgehenden Datenverkehr benutzt werden, und ob ein bestimmter Port eingehenden Datenverkehr akzeptiert. Die folgenden Protokolle werden von man:lagg[4] unterstützt: Failover (Ausfallsicherheit):: Dieser Modus sendet und empfängt Datenverkehr nur auf dem Masterport. Sollte der Masterport nicht zur Verfügung stehen, wird der nächste aktive Port verwendet. Der zuerst hinzugefügte Adapter der virtuellen Schnittstelle wird zum Masterport, jeder weitere Adapter dient als Gerät zur Ausfallsicherheit. Wenn ein Failover auf einem Nicht-Master Port stattfindet, wird der ursprüngliche Port wieder zum Master-Port, sobald er wieder verfügbar ist. fec / loadbalance (Lastverteilung):: Cisco(R) Fast EtherChannel(R) (FEC) findet sich auf älteren Cisco(R) Switches. Es bietet eine statische Konfiguration und handelt weder Aggregation mit der Gegenstelle aus, noch werden Frames zur Überwachung der Verbindung ausgetauscht. Wenn der Switch LACP unterstützt, sollte diese Option auch verwendet werden. lacp:: Das IEEE(R) 802.3ad Link-Aggregation Control Protokoll (LACP). Mit LACP wird eine Menge von aggregierbaren Verbindungen mit der Gegenstelle in einer oder mehreren Link Aggregated Groups (LAG) ausgehandelt. Jede LAG besteht aus Ports der gleichen Geschwindigkeit, eingestellt auf Voll-Duplex-Betrieb. Der Verkehr wird über die Ports in der LAG mit der größten Gesamtgeschwindigkeit balanciert. Typischerweise gibt es nur eine LAG, die alle Ports enthält. Im Falle von Änderungen in der physischen Anbindung wird LACP schnell zu einer neuen Konfiguration konvergieren. + LACP balanciert ausgehenden Verkehr über die aktiven Ports basierend auf der gehashten Protokollheaderinformation und akzeptiert eingehenden Verkehr auf jedem aktiven Port. Der Hash beinhaltet die Ethernet-Quell- und Zieladresse, und, soweit verfügbar, den VLAN-Tag, sowie die IPv4 oder IPv6 Quell- und Zieladresse. roundrobin:: Dieser Modus verteilt ausgehenden Verkehr mittels einer Round-Robin-Zuteilung über alle aktiven Ports und akzeptiert eingehenden Verkehr auf jedem aktiven Port. Da dieser Modus die Reihenfolge von Ethernet-Rahmen verletzt, sollte er mit Vorsicht eingesetzt werden. === Beispiele Dieser Abschnitt zeigt, wie man einen Cisco(R) Switch und ein FreeBSD-System für LACP Load Balancing konfiguriert. Weiterhin wird gezeigt, wie man zwei Ethernet-Schnittstellen, sowie eine Ethernet- und eine Drahtlos-Schnittstelle für den Failover-Modus konfigurieren kann. [[networking-lacp-aggregation-cisco]] .LACP Aggregation mit einem Cisco(R) Switch [example] ==== Dieses Beispiel verbindet zwei man:fxp[4] Ethernet-Schnittstellen einer FreeBSD-Maschine zu den ersten zwei Ethernet-Ports auf einem Cisco(R) Switch als eine einzelne, lastverteilte und ausfallsichere Verbindung. Weitere Adapter können hinzugefügt werden, um den Durchsatz zu erhöhen und die Ausfallsicherheit zu steigern. Ersetzen Sie die Namen der Cisco(R)-Ports, Ethernet-Geräte, channel-group Nummern und IP-Adressen im Beispiel durch Namen, die mit Ihrer lokalen Konfiguration übereinstimmen. Da die Reihenfolge der Frames bei Ethernet zwingend eingehalten werden muss, fließt auch jeglicher Verkehr zwischen zwei Stationen über den gleichen physischen Kanal, was die maximale Geschwindigkeit der Verbindung auf die eines einzelnen Adapters beschränkt. Der Übertragungsalgorithmus versucht, so viele Informationen wie möglich zu verwenden, um die verschiedenen Verkehrsflüsse zu unterscheiden und balanciert diese über die verfügbaren Adapter. Fügen Sie auf dem Cisco(R)-Switch die Adapter _FastEthernet0/1_ und _FastEthernet0/2_ zu der channel-group _1_ hinzu: [source,shell] .... interface FastEthernet0/1 channel-group 1 mode active channel-protocol lacp ! interface FastEthernet0/2 channel-group 1 mode active channel-protocol lacp .... Erstellen Sie auf der FreeBSD Maschine die man:lagg[4]-Schnittstelle unter Verwendung von _fxp0_ und _fxp1_ und starten Sie die Schnittstelle mit der IP-Adresse _10.0.0.3/24_: [source,shell] .... # ifconfig fxp0 up # ifconfig fxp1 up # ifconfig lagg0 create # ifconfig lagg0 up laggproto lacp laggport fxp0 laggport fxp1 10.0.0.3/24 .... Überprüfen Sie den Status der virtuellen Schnittstelle: [source,shell] .... # ifconfig lagg0 lagg0: flags=8843 metric 0 mtu 1500 options=8 ether 00:05:5d:71:8d:b8 inet 10.0.0.3 netmask 0xffffff00 broadcast 10.0.0.255 media: Ethernet autoselect status: active laggproto lacp laggport: fxp1 flags=1c laggport: fxp0 flags=1c .... Ports, die als _ACTIVE_ markiert sind, sind Teil der aktiven Aggregations-Gruppe, die mit dem Switch ausgehandelt wurde. Der Verkehr wird über diese Gruppe übertragen und empfangen. Benutzen Sie man:ifconfig[8] mit `-v`, um sich die LAG-Bezeichner anzeigen zu lassen. Um den Status der Ports auf dem Switch anzuzeigen, benutzen Sie `show lacp neighbor`: [source,shell] .... switch# show lacp neighbor Flags: S - Device is requesting Slow LACPDUs F - Device is requesting Fast LACPDUs A - Device is in Active mode P - Device is in Passive mode Channel group 1 neighbors Partner's information: LACP port Oper Port Port Port Flags Priority Dev ID Age Key Number State Fa0/1 SA 32768 0005.5d71.8db8 29s 0x146 0x3 0x3D Fa0/2 SA 32768 0005.5d71.8db8 29s 0x146 0x4 0x3D .... Benutzen Sie `show lacp neighbor detail`, um weitere Informationen zu erhalten. Damit diese Konfiguration auch nach einem Neustart erhalten bleibt, fügen Sie auf dem FreeBSD-System folgende Einträge in [.filename]#/etc/rc.conf# hinzu: [.programlisting] .... ifconfig_fxp0="up" ifconfig_fxp1="up" cloned_interfaces="lagg0 ifconfig_lagg0="laggproto lacp laggport fxp0 laggport fxp1 10.0.0.3/24" .... ==== [[networking-lagg-failover]] .Ausfallsicherer Modus [example] ==== Der ausfallsichere Modus kann verwendet werden, um zu einer zweiten Schnittstelle zu wechseln, sollte die Verbindung mit der Master-Schnittstelle ausfallen. Um den ausfallsicheren Modus zu konfigurieren, aktivieren Sie zunächst die zugrunde liegenden physikalischen Schnittstellen. Erstellen Sie dann die man:lagg[4]-Schnittstelle mit _fxp0_ als Master-Schnittstelle und _fxp1_ als sekundäre Schnittstelle. Der virtuellen Schnittstelle wird die IP-Adresse _10.0.0.15/24_ zugewiesen: [source,shell] .... # ifconfig fxp0 up # ifconfig fxp1 up # ifconfig lagg0 create # ifconfig lagg0 up laggproto failover laggport fxp0 laggport fxp1 10.0.0.15/24 .... Die virtuelle Schnittstelle sollte in etwa so aussehen: [source,shell] .... # ifconfig lagg0 lagg0: flags=8843 metric 0 mtu 1500 options=8 ether 00:05:5d:71:8d:b8 inet 10.0.0.15 netmask 0xffffff00 broadcast 10.0.0.255 media: Ethernet autoselect status: active laggproto failover laggport: fxp1 flags=0<> laggport: fxp0 flags=5 .... Der Verkehr wird auf _fxp0_ übertragen und empfangen. Wenn die Verbindung auf _fxp0_ abbricht, wird _fxp1_ die Verbindung übernehmen. Sobald die Verbindung auf der Master-Schnittstelle wiederhergestellt ist, wird diese wieder als aktive Schnittstelle genutzt. Damit diese Konfiguration auch nach einem Neustart erhalten bleibt, fügen Sie folgende Einträge in [.filename]#/etc/rc.conf# hinzu: [.programlisting] .... ifconfig_fxp0="up" ifconfig_fxp1="up" cloned_interfaces="lagg0 ifconfig_lagg0="laggproto failover laggport fxp0 laggport fxp1 10.0.0.15/24" .... ==== [[networking-lagg-wired-and-wireless]] .Failover Modus zwischen Ethernet- und drahtlosen Schnittstellen [example] ==== Für Laptop-Benutzer ist es normalerweise wünschenswert, "wireless" als sekundäre Schnittstelle einzurichten, die verwendet wird, wenn die Ethernet-Verbindung nicht verfügbar ist. Mit man:lagg[4] ist es möglich, ein Failover mit einer IP-Adresse zu konfigurieren, welches die Ethernet-Verbindung aus Performance- und Sicherheitsgründen bevorzugt, während es gleichzeitig möglich bleibt, Daten über die drahtlose Verbindung zu übertragen. Dies wird erreicht, indem die MAC-Adresse der Ethernet-Schnittstelle mit der MAC Adresse der drahtlosen Schnittstelle überschrieben wird. [NOTE] -**** +==== Theoretisch kann die Ethernet- oder die drahtlose MAC-Adresse so geändert werden, dass sie mit der jeweils anderen Adresse übereinstimmt. Bei einigen drahtlosen Schnittstellen fehlt jedoch die Unterstützung für das Überschreiben der MAC-Adresse. Daher wird empfohlen, die MAC-Adresse der Ethernet-Schnittstelle für diesen Zweck zu überschreiben. -**** +==== [NOTE] -**** +==== Wenn der Treiber für die drahtlose Schnittstelle nicht im `GENERIC`-Kernel oder in einem angepassten Kernel enthalten ist, kann unter FreeBSD {rel121-current} mit `_driver__load="YES"` die entsprechende [.filename]#.ko#-Datei in [.filename]#/boot/loader.conf# geladen werden. Dann muss das System neu gestartet werden. Ein anderer, besserer Weg ist es, den Treiber über [.filename]#/etc/rc.conf# zu laden, indem Sie ihn zu `kld_list` (siehe man:rc.conf[5]) hinzufügen und dann das System neu starten. Dies ist notwendig, da sonst der Treiber zum Zeitpunkt der Konfiguration der man:lagg[4]-Schnittstelle noch nicht geladen ist. -**** +==== In diesem Beispiel ist die Ethernet-Schnittstelle _re0_ der Master und die drahtlose Schnittstelle _wlan0_ der Failover. Die Schnittstelle _wlan0_ wurde aus der physischen Schnittstelle _ath0_ erstellt, und die Ethernet-Schnittstelle wird mit der MAC-Adresse der drahtlosen Schnittstelle konfiguriert. Im ersten Schritt wird die MAC-Adresse der drahtlosen Schnittstelle ermittelt: [source,shell] .... # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether b8:ee:65:5b:32:59 groups: wlan ssid Bbox-A3BD2403 channel 6 (2437 MHz 11g ht/20) bssid 00:37:b7:56:4b:60 regdomain ETSI country FR indoor ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 2:128-bit txpower 30 bmiss 7 scanvalid 60 protmode CTS ampdulimit 64k ampdudensity 8 shortgi -stbctx stbcrx -ldpc wme burst roaming MANUAL media: IEEE 802.11 Wireless Ethernet MCS mode 11ng status: associated nd6 options=29 .... Ersetzen Sie _ath0_ durch den Namen der drahtlosen Schnittstelle. Die `ether`-Zeile wird die MAC-Adresse der angegebenen Schnittstelle enthalten. Ändern Sie nun die MAC-Adresse der zugrunde liegenden Ethernet-Schnittstelle: [source,shell] .... # ifconfig re0 ether b8:ee:65:5b:32:59 .... Starten Sie die drahtlose Schnittstelle, aber ohne eine IP-Adresse zu setzen. Ersetzen Sie _FR_ durch den entsprechenden Ländercode: [source,shell] .... # ifconfig wlan0 create wlandev iwn0 country FR ssid my_router up .... Stellen Sie sicher, dass die _re0_-Schnittstelle aktiv ist. Erstellen Sie die man:lagg[4]-Schnittstelle mit _re0_ als Master und _wlan0_ als Failover: [source,shell] .... # ifconfig re0 up # ifconfig lagg0 create # ifconfig lagg0 up laggproto failover laggport re0 laggport wlan0 .... Die virtuelle Schnittstelle sollte in etwa so aussehen: [source,shell] .... # ifconfig lagg0 lagg0: flags=8843 metric 0 mtu 1500 options=8 ether b8:ee:65:5b:32:59 laggproto failover lagghash l2,l3,l4 laggport: re0 flags=5 laggport: wlan0 flags=0<> groups: lagg media: Ethernet autoselect status: active .... Starten Sie dann den DHCP-Client, um eine IP-Adresse zu erhalten: [source,shell] .... # dhclient lagg0 .... Damit diese Konfiguration auch nach einem Neustart erhalten bleibt, fügen Sie folgende Einträge in [.filename]#/etc/rc.conf# hinzu: [.programlisting] .... ifconfig_re0="ether b8:ee:65:5b:32:59" wlans_ath0="wlan0" ifconfig_wlan0="WPA" create_args_wlan0="country FR" cloned_interfaces="lagg0" ifconfig_lagg0="up laggproto failover laggport re0 laggport wlan0 DHCP" .... ==== [[network-diskless]] == Plattenloser Betrieb mit PXE Das Intel(R) Preboot eXecution Environment (PXE) erlaubt es dem Betriebssystem über das Netzwerk zu starten. Zum Beispiel kann ein FreeBSD-System, ohne lokale Festplatte, über das Netzwerk gestartet und betrieben werden. Die Dateisysteme werden dabei über einen NFS-Server eingehangen. PXE-Unterstützung steht in der Regel im BIOS zur Verfügung. Um PXE beim Systemstart zu verwenden, müssen Sie im BIOS des Rechners die Option `Über das Netzwerk starten` aktivieren. Alternativ können Sie während der PC-Initialisierung auch eine Funktionstaste drücken. Um die notwendigen Dateien für ein Betriebssystem für den Start über das Netzwerk bereitzustellen, benötigt ein PXE-Setup einen richtig konfigurierten DHCP-, TFTP- und NFS-Server, wobei: * Die initialen Parameter, wie IP-Adresse, Dateiname und Speicherort der ausführbaren Bootdateien, Servername sowie Root-Pfad vom DHCP-Server bezogen werden. * Der Loader für das Betriebssystem über TFTP gestartet wird. * Die Dateisysteme über NFS geladen werden. Sobald das Gastsystem über PXE startet, erhält es vom DHCP-Server Informationen, wo der initiale Bootloader per TFTP zu bekommen ist. Nachdem das Gastsystem diese Informationen erhalten hat, lädt es den Bootloader über TFTP herunter und führt diesen anschließend aus. In FreeBSD ist [.filename]#/boot/pxeboot# der Bootloader. Nachdem [.filename]#/boot/pxeboot# ausgeführt und der FreeBSD-Kernel geladen wurde, wird mit dem Rest der FreeBSD-Bootsequenz, wie in crossref:boot[boot,FreeBSDs Bootvorgang] beschrieben, fortgefahren. Dieser Abschnitt beschreibt, wie Sie diese Dienste auf einem FreeBSD-System so konfigurieren, sodass andere Systeme FreeBSD über PXE starten können. Weitere Informationen finden Sie in man:diskless[8]. [CAUTION] ==== Wie beschrieben, ist das System, welches diese Dienste bereitstellt, unsicher. Daher sollte es in einem geschützten Bereich des Netzwerks aufgestellt und von anderen Hosts als nicht vertrauenswürdig eingestuft werden. ==== [[network-pxe-nfs]] === Konfiguration der PXE-Umgebung Die in diesem Abschnitt dargestellten Schritte konfigurieren die in FreeBSD enthaltenen NFS- und TFTP-Server. Der folgende Abschnitt beschreibt die Installation und Konfiguration des DHCP-Servers. In diesem Beispiel verwenden wir [.filename]#/b/tftpboot/FreeBSD/install#, welches die Dateien für PXE-Benutzer enthält. Es ist wichtig, dass dieses Verzeichnis existiert und das der gleiche Verzeichnisname ebenfalls in [.filename]#/etc/inetd.conf# und [.filename]#/usr/local/etc/dhcpd.conf# gesetzt wird. [.procedure] . Erstellen Sie das Root-Verzeichnis, welches eine FreeBSD-Installation enthält und über NFS eingehangen werden kann: + [source,shell] .... # export NFSROOTDIR=/b/tftpboot/FreeBSD/install # mkdir -p ${NFSROOTDIR} .... . Aktivieren Sie den NFS-Server, indem Sie folgende Zeile in [.filename]#/etc/rc.conf# hinzufügen: + [.programlisting] .... nfs_server_enable="YES" .... -+ ++ Exportieren Sie das Root-Verzeichnis über NFS, indem Sie folgende Zeile in [.filename]#/etc/exports# hinzufügen: + [.programlisting] .... /b -ro -alldirs -maproot=root .... . Starten Sie den NFS-Server: + [source,shell] .... # service nfsd start .... . Aktivieren Sie man:inetd[8], indem Sie folgende Zeile in [.filename]#/etc/rc.conf# hinzufügen: + [.programlisting] .... inetd_enable="YES" .... . Kommentieren Sie die folgende Zeile in [.filename]#/etc/inetd.conf# aus, indem Sie sicherstellen, dass die Zeile nicht mit einem `#`-Zeichen beginnt: + [.programlisting] .... tftp dgram udp wait root /usr/libexec/tftp tftp -l -s /b/tftpboot .... + [NOTE] ==== Einige PXE-Versionen benötigen die TCP-Version von TFTP. In diesem Fall können Sie die zweite `tftp`-Zeile, welche `stream tcp` enthält, auskommentieren. ==== . Starten Sie man:inetd[8]: + [source,shell] .... # service inetd start .... . Installieren Sie das Basissystem nach [.filename]#${NFSROOTDIR}#, indem Sie die offiziellen Archive entpacken, oder ein neues Basissystem und einen FreeBSD-Kernel erstellen. Detaillierte Anweisungen hierzu finden Sie im crossref:cutting-edge[makeworld,“FreeBSD aus den Quellen aktualisieren”]. Vergessen Sie jedoch nicht `DESTDIR=_${NFSROOTDIR}_` hinzuzufügen, wenn Sie die Kommandos `make installkernel` und `make installworld` ausführen. . Testen Sie den TFTP-Server und vergewissern Sie sich, dass Sie den Bootloader herunterladen können, der über PXE bereitgestellt wird: + [source,shell] .... # tftp localhost tftp> get FreeBSD/install/boot/pxeboot Received 264951 bytes in 0.1 seconds .... . Bearbeiten Sie [.filename]#${NFSROOTDIR}/etc/fstab# und erstellen Sie einen Eintrag, um das Root-Dateisystem über NFS einzuhängen: + [.programlisting] .... # Device Mountpoint FSType Options Dump Pass$ myhost.example.com:/b/tftpboot/FreeBSD/install / nfs ro 0 0 .... -+ ++ Ersetzen Sie _myhost.example.com_ durch den Hostnamen oder die IP-Adresse des NFS-Servers. In diesem Beispiel wird das Root-Dateisystem schreibgeschützt eingehangen, um ein potenzielles Löschen des Inhalts durch die NFS-Clients zu verhindern. . Setzen Sie das root-Passwort in der PXE-Umgebung für Client-Maschinen, die über PXE starten: + [source,shell] .... # chroot ${NFSROOTDIR} # passwd .... . Falls erforderlich, aktivieren Sie man:ssh[1] root-Logins für Client-Maschinen, die über PXE starten, indem Sie die Option `PermitRootLogin` in [.filename]#${NFSROOTDIR}/etc/ssh/sshd_config# aktivieren. Dies ist in man:sshd_config[5] dokumentiert. . Führen Sie alle weiteren Anpassungen der PXE-Umgebung in [.filename]#${NFSROOTDIR}# durch, wie zum Beispiel die Installation weiterer Pakete, oder dass Bearbeiten der Passwortdatei mit man:vipw[8]. Booten Sie von einem NFS-Root-Volume, so erkennt [.filename]#/etc/rc# dies und startet daraufhin das [.filename]#/etc/rc.initdiskless# Skript. Lesen Sie die Kommentare in diesem Skript um zu verstehen, was dort vor sich geht. Weil das NFS-Root-Verzeichnis schreibgeschützt ist, wir aber Schreibzugriff für [.filename]#/etc# und [.filename]#/var# benötigen, müssen wir diese Verzeichnisse über Speicher-Dateisysteme (memory backed file system) einbinden. [source,shell] .... # chroot ${NFSROOTDIR} # mkdir -p conf/base # tar -c -v -f conf/base/etc.cpio.gz --format cpio --gzip etc # tar -c -v -f conf/base/var.cpio.gz --format cpio --gzip var .... Wenn das System bootet, werden Speicher-Dateisysteme für [.filename]#/etc# und [.filename]#/var# erstellt und eingehangen. Anschließend wird der Inhalt der [.filename]#cpio.gz#-Dateien in diese Dateisysteme kopiert. Standardmäßig haben diese Dateisysteme eine maximale Kapazität von 5 Megabyte. Wenn die Archive nicht passen, was für gewöhnlich bei [.filename]#/var# der Fall ist, erhöhen Sie die Kapazität indem Sie die Anzahl der benötigten 512 Byte Sektoren (5 Megabyte sind 10240 Sektoren) in [.filename]#${NFSROOTDIR}/conf/base/etc/md_size# und [.filename]#${NFSROOTDIR}/conf/base/var/md_size# für die Dateisysteme [.filename]#/etc# und [.filename]#/var# eintragen. [[network-pxe-setting-up-dhcp]] === Konfiguration des DHCP-Servers Der DHCP-Server muss nicht auf derselben Maschine laufen wie der TFTP- und NFS-Server, aber er muss über das Netzwerk erreichbar sein. DHCP ist nicht Bestandteil des FreeBSD Basissystems, kann jedoch über den Port package:net/isc-dhcp44-server[] oder als Paket nachinstalliert werden. Einmal installiert, bearbeiten Sie die Konfigurationsdatei [.filename]#/usr/local/etc/dhcpd.conf#. Konfigurieren Sie die `next-server`, `filename` und `root-path` Einstellungen, wie in diesem Beispiel zu sehen ist: [.programlisting] .... subnet 192.168.0.0 netmask 255.255.255.0 { range 192.168.0.2 192.168.0.3; option subnet-mask 255.255.255.0; option routers 192.168.0.1; option broadcast-address 192.168.0.255; option domain-name-servers 192.168.35.35, 192.168.35.36; option domain-name "example.com"; # IP address of TFTP server next-server 192.168.0.1; # path of boot loader obtained via tftp filename "FreeBSD/install/boot/pxeboot"; # pxeboot boot loader will try to NFS mount this directory for root FS option root-path "192.168.0.1:/b/tftpboot/FreeBSD/install/"; } .... Die Anweisung `next-server` wird benutzt, um die IP-Adresse des TFTP-Servers anzugeben. Die Anweisung `filename` definiert den Pfad zu [.filename]#/boot/pxeboot#. Da hier der relative Dateiname verwendet wird, bedeutet das, dass [.filename]#/b/tftpboot# nicht im Pfad enthalten ist. Die Option `root-path` bestimmt den Pfad zum NFS root-Dateisystem. Sobald die Änderungen gespeichert werden, aktivieren Sie DHCP beim Systemstart, indem Sie die folgende Zeile in [.filename]#/etc/rc.conf# hinzufügen: [.programlisting] .... dhcpd_enable="YES" .... Starten Sie anschließend den DHCP-Dienst: [source,shell] .... # service isc-dhcpd start .... === Fehlersuche bei PXE Problemen Sobald alle Dienste konfiguriert und gestartet wurden, sollten PXE-Clients in der Lage sein, FreeBSD automatisch über das Netzwerk zu starten. Wenn ein bestimmter Client beim hochfahren keine Verbindung herstellen kann, sehen Sie im BIOS nach, ob die Option für den Start über das Netzwerk konfiguriert ist. Dieser Abschnitt gibt einige Tipps zu Fehlerbehebung und zeigt, wie Sie Konfigurationsprobleme eingrezen können für den Fall, dass PXE-Clients nicht in der Lage sind über das Netzwerk zu starten. [.procedure] . Benutzen Sie den package:net/wireshark[] Port um Fehler im Netzwerkverkehr während des Bootvorgangs von PXE zu finden. Der Bootvorgang wird im folgenden Diagramm schematisch dargestellt. + .PXE-Bootvorgang mit NFS Root Mount image::pxe-nfs.png[] . Schauen Sie in [.filename]#/var/log/xferlog# auf dem TFTP-Server und vergewissern Sie sich, dass die [.filename]#pxeboot#-Datei von der richtigen Adresse heruntergeladen wurde. Um die obige Konfiguration von [.filename]#/usr/local/etc/dhcpd.conf# zu testen, geben Sie folgendes ein: + [source,shell] .... # tftp 192.168.0.1 tftp> get FreeBSD/install/boot/pxeboot Received 264951 bytes in 0.1 seconds .... -+ ++ Weitere Informationen finden Sie in man:tftpd[8] und man:tftp[1]. Die `BUGS`-Sektionen dieser Seiten dokumentieren einige Einschränkungen von TFTP. . Achten Sie darauf, dass Sie das Root-Dateisystem über NFS einhängen können. Auch hier können Sie Ihre Einstellungen aus [.filename]#/usr/local/etc/dhcpd.conf# wie folgt testen: + [source,shell] .... # mount -t nfs 192.168.0.1:/b/tftpboot/FreeBSD/install /mnt .... [[network-ipv6]] == IPv6 IPv6 ist die neueste Version des bekannten IP-Protokolls, das auch als IPv4 bezeichnet wird. IPv6 bietet gegenüber IPv4 mehrere Vorteile sowie viele neue Funktionen: * IPv6 hat einen 128 Bit großen Adressraum, der 340.282.366.920.938.463.463.374.607.431.768.211.456 Adressen erlaubt. Dies behebt das Problem der immer knapper werdenden IPv4-Adressen und einer eventuellen Erschöpfung des IPv4-Adressraums. * Router speichern nur noch Netzwerk-Aggregationsadressen in ihren Routingtabellen. Dadurch reduziert sich die durchschnittliche Größe einer Routingtabelle auf 8192 Einträge. Dies ist mit den Problemen bei der Skalierbarkeit von IPv4 verbunden, da jeder zugeordnete Block von IPv4-Adressen erfordert, dass Routing-Informationen zwischen vielen Routern im Internet ausgetauscht werden müssen. Die Routing-Tabellen wurden mit der Zeit so groß, dass ein effizientes Routing jetzt kaum noch möglich ist. * Die automatische Konfiguration von Adressen, die im http://www.ietf.org/rfc/rfc2462.txt[ RFC2462] beschrieben wird. * Verpflichtende Multicast-Adressen. * Integriertes IPsec (IP-Security). * Eine vereinfachte Headerstruktur. * Unterstützung für mobile IP-Adressen. * Die Umwandlung von IPv4- in IPv6-Adressen. FreeBSD enthält die IPv6-Referenzimplementation von link:http//www.kame.net/[KAME] und erfüllt damit bereits alle für die Nutzung von IPv6 nötigen Voraussetzungen. Dieser Abschnitt konzentriert sich auf die Konfiguration und den Betrieb von IPv6. === Hintergrundinformationen zu IPv6-Adressen Es gibt verschiedene Arten von IPv6-Adressen: Unicast:: Ein Paket, das an eine Unicast-Adresse gesendet wird, kommt nur an der Schnittstelle an, die dieser Adresse zugeordnet ist. Anycast:: Anycast-Adressen unterscheiden sich in ihrer Syntax nicht von Unicast-Adressen, sie wählen allerdings aus mehreren Schnittstellen eine Schnittstelle aus. Ein für eine Anycast-Adresse bestimmtes Paket kommt an der nächstgelegenen (entsprechend der Router-Metrik) Schnittstelle an. Anycast-Adressen werden nur von Routern verwendet. Mulitcast:: Multicast-Adressen bestimmen Gruppen, denen mehrere Schnittstellen angehören. Ein Paket, das an eine Multicast-Adresse geschickt wird, kommt an allen Schnittstellen an, die zur Multicast-Gruppe gehören. Die von IPv4 bekannte Broadcast-Adresse (normalerweise `xxx.xxx.xxx.255`) wird bei IPv6 durch Multicast-Adressen verwirklicht. Die kanonische Form einer IPv6-Adresse lautet `x:x:x:x:x:x:x:x`, wobei jedes "x" für einen 16-Bit-Hexadezimalwert steht. Ein Beispiel für eine IPv6-Adresse wäre etwa `FEBC:A574:382B:23C1:AA49:4592:4EFE:9982`. Eine IPv6-Adresse enthält oft Teilzeichenfolgen aus lauter Nullen. Eine solche Zeichenfolge kann zu "::" verkürzt werden. Bis zu drei führende Nullen eines Hexquads können ebenfalls weggelassen werden. `fe80::1` entspricht also der Adresse `fe80:0000:0000:0000:0000:0000:0000:0001`. Eine weitere Möglichkeit ist die Darstellung der letzten 32 Bit in der bekannten IPv4-Notation. `2002::10.0.0.1` ist also eine andere Schreibweise für die (hexadezimale) kanonische Form `2002:0000:0000:0000:0000:0000:0a00:0001`, die wiederum der Adresse `2002::a00:1` entspricht. Benutzen Sie man:ifconfig[8], um die IPv6-Adresse eines FreeBSD-Systems anzuzeigen: [source,shell] .... # ifconfig rl0: flags=8943 mtu 1500 inet 10.0.0.10 netmask 0xffffff00 broadcast 10.0.0.255 inet6 fe80::200:21ff:fe03:8e1%rl0 prefixlen 64 scopeid 0x1 ether 00:00:21:03:08:e1 media: Ethernet autoselect (100baseTX ) status: active .... Bei `fe80::200:21ff:fe03:8e1%rl0` handelt es sich um eine automatisch konfigurierte link-local-Adresse. Sie wird im Rahmen der automatischen Konfiguration aus der MAC-Adresse erzeugt. Einige IPv6-Adressen sind reserviert. Eine Zusammenfassung dieser Adressen finden Sie in <>: [[reservedip6]] .Reservierte IPv6-Adressen [cols="1,1,1,1", frame="none", options="header"] |=== | IPv6-Adresse | Präfixlänge | Beschreibung | Anmerkungen |`::` |128 Bit |nicht festgelegt |entspricht `0.0.0.0` bei IPv4. |`::1` |128 Bit |Loopback-Adresse |entspricht `127.0.0.1` bei IPv4. |`::00:xx:xx:xx:xx` |96 Bit |Eingebettete IPv4-Adresse |Die niedrigen 32 Bit sind die kompatiblen IPv4-Adressen. |`::ff:xx:xx:xx:xx` |96 Bit |Eine auf IPv6 abgebildete IPv4-Adresse. |Die niedrigen 32 Bit sind IPv4-Adressen für Hosts, die kein IPv6 unterstützen. |`fe80::/10` |10 Bit |link-local |Entspricht 196.254.0.0/16 bei IPv4. |`fc00::/7` |7 Bit |unique-local |Diese einzigartigen Adressen sind für die lokale Kommunikation bestimmt und werden nur innerhalb von abgegrenzten Standorten (Sites) weitergeleitet. |`ff00::` |8 Bit |Multicast | |`2000::-3fff::` |3 Bit |Globaler Unicast |Alle globalen Unicast-Adressen stammen aus diesem Pool. Die ersten 3 Bit lauten `001`. |=== Weitere Informationen zum Aufbau von IPv6-Adressen finden Sie im http://www.ietf.org/rfc/rfc3513.txt[ RFC3513]. === IPv6 konfigurieren Um ein FreeBSD-System als IPv6-Client zu konfigurieren, fügen Sie folgende Zeile in [.filename]#/etc/rc.conf# ein: [.programlisting] .... ifconfig_rl0_ipv6="inet6 accept_rtadv" rtsold_enable="YES" .... Die erste Zeile ermöglicht der angegebenen Schnittstelle, Router-Advertisement-Nachrichten zu empfangen. Die zweite Zeile aktiviert den Router-Solicitation-Daemon man:rtsold[8]. Falls die Schnittstelle eine statisch zugewiesene IPv6-Adresse benötigt, fügen Sie einen Eintrag mit der statischen Adresse und dem zugehörigen Präfix für das Subnetz hinzu: [.programlisting] .... ifconfig_rl0_ipv6="inet6 2001:db8:4672:6565:2026:5043:2d42:5344 prefixlen 64" .... Um einen Standardrouter festzulegen, fügen Sie die Adresse hinzu: [.programlisting] .... ipv6_defaultrouter="2001:db8:4672:6565::1" .... === Verbindung zu einem Provider aufbauen Um sich mit anderen IPv6-Netzwerken zu verbinden, benötigen Sie einen Provider oder einen Tunnel, der IPv6 unterstützt: * Fragen Sie einen Internetprovider, ob er IPv6 anbietet. * http://www.tunnelbroker.net[Hurricane Electric] bietet weltweit IPv6-Tunnelverbindungen an. [NOTE] ==== Die Verwendung des Ports [.filename]#/usr/ports/net/freenet6# für Einwahlverbindungen. ==== Dieser Abschnitt beschreibt, wie Sie die Anweisungen eines Tunnel-Providers dauerhaft in [.filename]#/etc/rc.conf# einrichten. Der erste Eintrag in [.filename]#/etc/rc.conf# erzeugt die generische Tunnelschnittstelle [.filename]#gif0#: [.programlisting] .... cloned_interfaces="gif0" .... Als nächstes konfigurieren Sie die IPv4-Adressen der lokalen und entfernten Endpunkte. Ersetzen Sie _MY_IPv4_ADDR_ und _REMOTE_IPv4_ADDR_ durch die tatsächlichen IPv4-Adressen: [.programlisting] .... cloned_interfaces_gif0="MY_IPv4_ADDR REMOTE_IPv4_ADDR" .... Um die zugewiesene IPv6-Adresse als Endpunkt für den IPv6-Tunnel zu verwenden, fügen Sie folgende Zeile für FreeBSD 9._x_ (und neuer) ein: [.programlisting] .... ifconfig_gif0_ipv6="inet6 MY_ASSIGNED_IPv6_TUNNEL_ENDPOINT_ADDR" .... Legen Sie dann die Standardroute für das andere Ende des IPv6-Tunnels fest. Ersetzen Sie _MY_IPv6_REMOTE_TUNNEL_ENDPOINT_ADDR_ mit der Adresse des Standard-Gateways des Providers: [.programlisting] .... ipv6_defaultrouter="MY_IPv6_REMOTE_TUNNEL_ENDPOINT_ADDR" .... Wenn das FreeBSD-System IPv6-Verkehr zwischen dem Netzwerk und der Außenwelt routen muss, aktivieren Sie das Gateway mit dieser Zeile: [.programlisting] .... ipv6_gateway_enable="YES" .... === Bekanntmachung von Routen und automatische Rechnerkonfiguration Dieser Abschnitt beschreibt die Einrichtung von man:rtadvd[8], das Sie bei der Bekanntmachung der IPv6-Standardroute unterstützt. Um man:rtadvd[8] zu aktivieren, fügen Sie folgende Zeile in [.filename]#/etc/rc.conf# ein: [.programlisting] .... rtadvd_enable="YES" .... Es ist wichtig, die Schnittstelle anzugeben, über die IPv6-Routen bekanntgemacht werden sollen. Soll man:rtadvd[8] [.filename]#rl0# verwenden, ist folgender Eintrag nötig: [.programlisting] .... rtadvd_interfaces="rl0" .... Danach erzeugen Sie die Konfigurationsdatei [.filename]#/etc/rtadvd.conf#. Dazu ein Beispiel: [.programlisting] .... rl0:\ :addrs#1:addr="2001:db8:1f11:246::":prefixlen#64:tc=ether: .... Ersetzen Sie dabei [.filename]#fxp0# durch die zu verwendende Schnittstelle, und `2001:db8:1f11:246::` durch das entsprechend zugewiesene Präfix. Bei einem `/64`-Subnetz müssen keine weiteren Anpassungen vorgenommen werden. Anderenfalls muss `prefixlen#` auf den korrekten Wert gesetzt werden. === IPv6 und Abbildung von IPv6-Adressen Wenn IPv6 auf einem Server aktiviert ist, kann es für die Kommunikation erforderlich sein, IPv4-Adressen auf IPv6-Adressen abzubilden. Diese Kompatibilität erlaubt es, das IPv4-Adressen als IPv6-Adressen dargestellt werden. Die Kommunikation von IPv6-Anwendungen mit IPv4 und umgekehrt kann jedoch ein Sicherheitsrisiko darstellen. Diese Option dient nur der Kompatibilität und wird in den meisten Fällen nicht erforderlich sein. Die Option ermöglicht es IPv6-Anwendungen zusammen mit IPv4 in einer Dual-Stack-Umgebung zu funktionieren. Dies ist besonders nützlich für Anwendungen von Drittanbietern, die evtl. keine IPv6-Umgebungen unterstützen. Um diese Funktion zu aktivieren, fügen Sie folgendes in [.filename]#/etc/rc.conf# hinzu: [.programlisting] .... ipv6_ip4mapping="YES" .... Für einige Administratoren können die Informationen im RFC 3493 (Sektion 3.6 und 3.7) und RFC 4038 (Sektion 4.2) hilfreich sein. [[carp]] == Common Address Redundancy Protocol (CARP) Das Common Address Redundancy Protocol (CARP) erlaubt es, mehreren Rechnern die gleiche IP-Adresse und Virtual Host ID (VHID) zuzuweisen und _Hochverfügbarkeit_ bereitzustellen. Das bedeutet, dass ein oder mehrere Rechner ausfallen können und die anderen Rechner transparent einspringen, ohne dass die Benutzer etwas von einem Ausfall mitbekommen. Neben der gemeinsamen IP-Adresse, haben die jeweiligen Rechner auch eine eindeutige IP-Adresse zur Verwaltung und Konfiguration. Alle Maschinen, die sich eine IP-Adresse teilen, verwenden die gleiche VHID. Die VHID für jede einzelne IP-Adresse muss, entsprechend der Broadcast-Domäne der Netzwerkschnittstelle, eindeutig sein. Hochverfügbarkeit mit CARP ist in FreeBSD enthalten, jedoch unterscheidet sich die Konfiguration von der eingesetzten FreeBSD-Version. Dieser Abschnitt enthält die gleichen Konfigurationsdateien für verschiedene Versionen von FreeBSD. Dieses Beispiel konfiguriert eine Failover-Unterstützung mit drei Servern (mit jeweils eigener, eindeutiger IP-Adresse), die alle den gleichen Web-Inhalt anbieten. Es werden zwei verschiedene Master namens `hosta.example.org` und `hostb.example.org` benutzt, mit einem gemeinsamen Backup namens `hostc.example.org`. Die Lastverteilung dieser Maschinen wird dabei über Round RobinDNS konfiguriert. Mit Ausnahme des Hostnamens und der IP-Management-Adresse sind Master- und Backup-Maschinen identisch konfiguriert. Die Server müssen die gleiche Konfiguration und die gleichen Dienste aktiviert haben. Tritt ein Failover auf, können Anfragen an den Dienst mit der gemeinsam genutzten IP-Adresse nur dann richtig beantwortet werden, wenn der Backup-Server Zugriff auf denselben Inhalt hat. Die Backup-Maschine verfügt über zwei zusätzliche CARP-Schnittstellen, eine für jede IP-Adresse des Master-Content-Servers. Sobald ein Fehler auftritt, übernimmt der Backup-Server die IP-Adresse des ausgefallenen Master-Servers. [[carp-10x]] === CARP mit FreeBSD 10 (und neuer) benutzen Unterstützung für CARP erhalten Sie durch das Laden des Kernelmoduls [.filename]#carp.ko# in [.filename]#/boot/loader.conf#: [.programlisting] .... carp_load="YES" .... So laden Sie das Modul ohne Neustart: [source,shell] .... # kldload carp .... Benutzer, die einen angepassten Kernel verwenden möchten, müssen die folgende Zeile in die Konfigurationsdatei aufnehmen. Anschließend muss der Kernel, wie in crossref:kernelconfig[kernelconfig,Konfiguration des FreeBSD-Kernels] beschrieben, neu gebaut werden: [.programlisting] .... device carp .... Hostname, IP-Management-Adresse, Subnetzmaske, gemeinsame IP-Adresse und VHID werden durch Einträge in [.filename]#/etc/rc.conf# gesetzt. Dieses Beispiel ist für `hosta.example.org`: [.programlisting] .... hostname="hosta.example.org" ifconfig_em0="inet 192.168.1.3 netmask 255.255.255.0" ifconfig_em0_alias0="inet vhid 1 pass testpass alias 192.168.1.50/32" .... Die nächsten Einträge sind für `hostb.example.org`. Da der Rechner einen zweiten Master darstellt, verwendet er eine andere gemeinsame IP-Adresse und VHID. Die mittels `pass` angegebenen Passwörter müssen jedoch identisch sein, da CARP nur mit Systemen kommuniziert, die über das richtige Passwort verfügen. [.programlisting] .... hostname="hostb.example.org" ifconfig_em0="inet 192.168.1.4 netmask 255.255.255.0" ifconfig_em0_alias0="inet vhid 2 pass testpass alias 192.168.1.51/32" .... Die dritte Maschine, `hostc.example.org` ist so konfiguriert, das sie aktiviert wird, wenn einer der beiden Masterserver ausfällt. Diese Maschine ist mit zwei CARPVHIDs konfiguriert, eine für jede virtuelle IP-Adresse der beiden Master-Server. Die CARP advertising skew, `advskew` wird gesetzt, um sicherzustellen, dass sich der Backup-Server später ankündigt wie der Master-Server, da `advskew` die Rangfolge steuert für den Fall, dass mehrere Backup-Server zur Verfügung stehen. [.programlisting] .... hostname="hostc.example.org" ifconfig_em0="inet 192.168.1.5 netmask 255.255.255.0" ifconfig_em0_alias0="inet vhid 1 advskew 100 pass testpass alias 192.168.1.50/32" ifconfig_em0_alias1="inet vhid 2 advskew 100 pass testpass alias 192.168.1.51/32" .... Durch die beiden konfigurierten CARPVHIDs ist `hostc.example.org` in der Lage festzustellen, wenn einer der Master-Server nicht mehr reagiert. Wenn der Master-Server sich später ankündigt als der Backup-Server, übernimmt der Backup-Server die gemeinsame IP-Adresse, bis der Master-Server erneut verfügbar ist. [NOTE] ==== Auch wenn der ursprüngliche Master-Server wieder verfügbar wird, gibt `hostc.example.org` die virtuelle IP-Adresse nicht automatisch wieder frei. Dazu muss Preemption aktiviert werden. Preemption ist standardmäßig deaktiviert und wird über die man:sysctl[8]-Variable `net.inet.carp.preempt` gesteuert. Der Administrator kann den Backup-Server zwingen, die IP-Adresse an den Master zurückzugeben: [source,shell] .... # ifconfig em0 vhid 1 state backup .... ==== Sobald die Konfiguration abgeschlossen ist, muss das Netzwerk oder die Maschine neu gestartet werden. Hochverfügbarkeit ist nun aktiviert. Die Funktionalität von CARP kann, wie in der Manualpage man:carp[4] beschrieben, über verschiedene man:sysctl[8] Parameter kontrolliert werden. Mit dem Einsatz von man:devd[8] können weitere Aktionen zu CARP-Ereignissen ausgelöst werden. [[carp-9x]] === CARP mit FreeBSD 9 (und älter) benutzen Die Konfiguration für diese Versionen von FreeBSD ist ähnlich wie im vorhergehenden Abschnitt beschrieben, mit der Ausnahme, dass zuerst ein CARP-Gerät in der Konfiguration erstellt und bezeichnet werden muss. Unterstützung für CARP erhalten Sie durch das Laden des Kernelmoduls [.filename]#carp.ko# in [.filename]#/boot/loader.conf#: [.programlisting] .... if_carp_load="YES" .... So laden Sie das Modul ohne Neustart: [source,shell] .... # kldload carp .... Benutzer, die einen angepassten Kernel verwenden möchten, müssen die folgende Zeile in die Konfigurationsdatei aufnehmen. Anschließend muss der Kernel, wie in crossref:kernelconfig[kernelconfig,Konfiguration des FreeBSD-Kernels] beschrieben, neu gebaut werden: [.programlisting] .... device carp .... Als nächstes erstellen Sie auf jedem Rechner eine CARP-Schnittstelle: [source,shell] .... # ifconfig carp0 create .... Konfigurieren Sie Hostnamen, IP-Management-Adresse, die gemeinsam genutzte IP-Adresse und die VHID, indem Sie die erforderlichen Zeilen in [.filename]#/etc/rc.conf# hinzufügen. Da anstelle eines Alias eine virtuelles CARP-Gerät verwendet wird, wird die tatsächliche Subnetzmaske `/24` anstatt `/32` benutzt. Hier sind die Einträge für `hosta.example.org`: [.programlisting] .... hostname="hosta.example.org" ifconfig_fxp0="inet 192.168.1.3 netmask 255.255.255.0" cloned_interfaces="carp0" ifconfig_carp0="vhid 1 pass testpass 192.168.1.50/24" .... Beispiel für `hostb.example.org`: [.programlisting] .... hostname="hostb.example.org" ifconfig_fxp0="inet 192.168.1.4 netmask 255.255.255.0" cloned_interfaces="carp0" ifconfig_carp0="vhid 2 pass testpass 192.168.1.51/24" .... Die dritte Maschine, `hostc.example.org` ist so konfiguriert, das sie aktiviert wird, wenn einer der beiden Masterserver ausfällt: [.programlisting] .... hostname="hostc.example.org" ifconfig_fxp0="inet 192.168.1.5 netmask 255.255.255.0" cloned_interfaces="carp0 carp1" ifconfig_carp0="vhid 1 advskew 100 pass testpass 192.168.1.50/24" ifconfig_carp1="vhid 2 advskew 100 pass testpass 192.168.1.51/24" .... [NOTE] ==== Preemption ist im [.filename]#GENERIC#-Kernel deaktiviert. Haben Sie jedoch Preemption in einem angepassten Kernel aktiviert, dass `hostc.example.org` die virtuelle IP-Adresse nicht wieder an den Master-Server zurückgibt. Der Administrator kann jedoch den Backup-Server dazu zwingen, die übernommene IP-Adresse wieder an den Master-Server zurückzugeben: [source,shell] .... # ifconfig carp0 down && ifconfig carp0 up .... Dieser Befehl muss auf dem [.filename]#carp#-Gerät ausgeführt werden, dass dem betroffenen System zugeordnet ist. ==== Sobald die Konfiguration abgeschlossen ist, muss das Netzwerk oder die Maschine neu gestartet werden. Hochverfügbarkeit ist nun aktiviert. [[network-vlan]] == VLANs VLANs sind eine Möglichkeit ein Netzwerk virtuell in viele Subnetze zu unterteilen. Man spricht hier auch von Segmentierung. Jedes Subnetz hat seine eigene Broadcast-Domäne und ist von anderen VLANs isoliert. Unter FreeBSD müssen VLANs vom Treiber der Netzwerkkarte unterstützt werden. man:vlan[4] enthält eine Liste von Treibern mit integrierter VLAN-Unterstützung. Für die Konfiguration eines VLAN werden zwei Informationen benötigt: die verwendete Netzwerkschnittstelle und das VLAN-Tag. Das folgende Kommando konfiguriert ein VLAN mit der Netzwerkschnittstelle `em0` und dem VLAN-Tag `5`: [source,shell] .... # ifconfig em0.5 create vlan 5 vlandev em0 inet 192.168.20.20/24 .... [NOTE] ==== In diesem Beispiel fällt auf, dass der Name der Schnittstelle den Treibernamen und das VLAN-Tag enthält, getrennt durch einen Punkt. Diese Methode hat sich bewährt, da sie die Konfiguration von Systemen mit mehreren VLANs deutlich erleichtert. ==== Um VLANs beim Booten zu konfigurieren, muss [.filename]#/etc/rc.conf# angepasst werden. Für das obige Beispiel müssten folgende Zeilen in die Konfiguration aufgenommen werden: [.programlisting] .... vlans_em0="5" ifconfig_em0_5="inet 192.168.20.20/24" .... Das gleiche Schema kann benutzt werden, um weitere VLANs hinzuzufügen. Es ist sinnvoll, einer Schnittstelle einen symbolischen Namen zuzuweisen, so dass bei einem Wechsel der zugehörigen Hardware nur wenige Konfigurationsvariablen aktualisiert werden müssen. Nehmen wir beispielsweise an, dass Überwachungskameras im VLAN1 auf `em0` betrieben werden. Wenn später die Karte `em0` durch eine Karte ersetzt wird, die den man:ixgb[4] Treiber verwendet, müssen nicht alle Referenzen auf `em0.1` durch `ixgb0.1` ersetzt werden. Der folgende Befehl konfiguriert VLAN `5` auf der Netzwerkkarte `em0`. Die Schnittstelle bekommt den Namen `cameras` und eine IP-Adresse `_192.168.20.20_` mit einem `24`-Bit Präfix. [source,shell] .... # ifconfig em0.5 create vlan 5 vlandev em0 name cameras inet 192.168.20.20/24 .... Dieser Befehl konfiguriert die Schnittstelle mit dem Namen `video`: [source,shell] .... # ifconfig video.5 create vlan 5 vlandev video name cameras inet 192.168.20.20/24 .... Um die Änderungen beim Booten anzuwenden, fügen Sie folgenden Zeilen in [.filename]#/etc/rc.conf# ein: [.programlisting] .... vlans_video="cameras" create_args_cameras="vlan 5" ifconfig_cameras="inet 192.168.20.20/24" .... diff --git a/documentation/content/en/books/dev-model/_index.adoc b/documentation/content/en/books/dev-model/_index.adoc index 1f56b814b0..04bbac61e1 100644 --- a/documentation/content/en/books/dev-model/_index.adoc +++ b/documentation/content/en/books/dev-model/_index.adoc @@ -1,1208 +1,1208 @@ --- title: A project model for the FreeBSD Project authors: - author: Niklas Saers copyright: 2002-2005 Niklas Saers description: A formal study of the organization of the FreeBSD project trademarks: ["freebsd", "ibm", "ieee", "adobe", "intel", "linux", "microsoft", "opengroup", "sun", "netbsd", "general"] bookOrder: 45 tags: ["model", "project model", "FreeBSD"] layout: single --- //// Copyright (c) 2002-2005 Niklas Saers All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //// = A project model for the FreeBSD Project :doctype: book :toc: macro :toclevels: 2 :icons: font :sectnums: :partnums: :source-highlighter: rouge :experimental: :images-path: books/dev-model/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../images/{images-path} include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] include::../../../../../shared/asciidoctor.adoc[] endif::[] ''' toc::[] [[foreword]] [.abstract-title] Foreword Up until now, the FreeBSD project has released a number of described techniques to do different parts of work. However, a project model summarising how the project is structured is needed because of the increasing amount of project members. footnote:[This goes hand-in-hand with Brooks' law that adding another person to a late project will make it later since it will increase the communication needs . A project model is a tool to reduce the communication needs.] This paper will provide such a project model and is donated to the FreeBSD Documentation project where it can evolve together with the project so that it can at any point in time reflect the way the project works. -It is based on [crossref:dev-model[thesis, Saers,2003]]. +It is based on [ crossref:dev-model[thesis, Saers 2003] ]. I would like to thank the following people for taking the time to explain things that were unclear to me and for proofreading the document. * Andrey A. Chernov mailto:ache@freebsd.org[ache@freebsd.org] * Bruce A. Mah mailto:bmah@freebsd.org[bmah@freebsd.org] * Dag-Erling Smørgrav mailto:des@freebsd.org[des@freebsd.org] * Giorgos Keramidas mailto:keramida@freebsd.org[keramida@freebsd.org] * Ingvil Hovig mailto:ingvil.hovig@skatteetaten.no[ingvil.hovig@skatteetaten.no] * Jesper Holck mailto:jeh.inf@cbs.dk[jeh.inf@cbs.dk] * John Baldwin mailto:jhb@freebsd.org[jhb@freebsd.org] * John Polstra mailto:jdp@freebsd.org[jdp@freebsd.org] * Kirk McKusick mailto:mckusick@freebsd.org[mckusick@freebsd.org] * Mark Linimon mailto:linimon@freebsd.org[linimon@freebsd.org] * Marleen Devos * Niels Jørgenssen mailto:nielsj@ruc.dk[nielsj@ruc.dk] * Nik Clayton mailto:nik@freebsd.org[nik@freebsd.org] * Poul-Henning Kamp mailto:phk@freebsd.org[phk@freebsd.org] * Simon L. Nielsen mailto:simon@freebsd.org[simon@freebsd.org] [[overview]] == Overview A project model is a means to reduce the communications overhead in a project. -As shown by [crossref:dev-model[brooks, Brooks, 1995]], increasing the number of project participants increases the communication in the project exponentially. +As shown by [ crossref:dev-model[brooks, Brooks 1995] ], increasing the number of project participants increases the communication in the project exponentially. FreeBSD has during the past few years increased both its mass of active users and committers, and the communication in the project has risen accordingly. This project model will serve to reduce this overhead by providing an up-to-date description of the project. During the Core elections in 2002, Mark Murray stated "I am opposed to a long rule-book, as that satisfies lawyer-tendencies, and is counter to the technocentricity that the project so badly needs." -[crossref:dev-model[bsd-election2002, FreeBSD, 2002B]]. +[ crossref:dev-model[bsd-election2002, FreeBSD 2002B] ]. This project model is not meant to be a tool to justify creating impositions for developers, but as a tool to facilitate coordination. It is meant as a description of the project, with an overview of how the different processes are executed. It is an introduction to how the FreeBSD project works. The FreeBSD project model will be described as of July 1st, 2004. -It is based on the Niels Jørgensen's paper [crossref:dev-model[jorgensen2001, Jørgensen, 2001]], FreeBSD's official documents, discussions on FreeBSD mailing lists and interviews with developers. +It is based on the Niels Jørgensen's paper [ crossref:dev-model[jorgensen2001, Jørgensen 2001] ], FreeBSD's official documents, discussions on FreeBSD mailing lists and interviews with developers. After providing definitions of terms used, this document will outline the organisational structure (including role descriptions and communication lines), discuss the methodology model and after presenting the tools used for process control, it will present the defined processes. Finally it will outline major sub-projects of the FreeBSD project. -[crossref:dev-model[freebsd-developer-handbook, FreeBSD, 2002A]] Section 1.2 and 1.3 give the vision and the architectural guidelines for the project. +[ crossref:dev-model[freebsd-developer-handbook, FreeBSD 2002A] ] Section 1.2 and 1.3 give the vision and the architectural guidelines for the project. The vision is "To produce the best UNIX-like operating system package possible, with due respect to the original software tools ideology as well as usability, performance and stability." The architectural guidelines help determine whether a problem that someone wants to be solved is within the scope of the project [[definitions]] == Definitions [[ref-activity]] === Activity An "activity" is an element of work performed during the course of a project -[crossref:dev-model[ref-pmbok, PMI, 2000]]. +[ crossref:dev-model[ref-pmbok, PMI 2000] ]. It has an output and leads towards an outcome. Such an output can either be an input to another activity or a part of the process' delivery. [[def-process]] === Process A "process" is a series of activities that lead towards a particular outcome. A process can consist of one or more sub-processes. An example of a process is software design. [[ref-hat]] === Hat A "hat" is synonymous with role. A hat has certain responsibilities in a process and for the process outcome. The hat executes activities. It is well defined what issues the hat should be contacted about by the project members and people outside the project. [[ref-outcome]] === Outcome An "outcome" is the final output of the process. This is synonymous with deliverable, that is defined as "any measurable, tangible, verifiable outcome, result or item that must be produced to complete a project or part of a project. Often used more narrowly in reference to an external deliverable, which is a deliverable that is subject to approval by the project sponsor or customer" by -[crossref:dev-model[ref-pmbok, PMI, 2000]]. +[ crossref:dev-model[ref-pmbok, PMI 2000] ]. Examples of outcomes are a piece of software, a decision made or a report written. [[ref-freebsd]] === FreeBSD When saying "FreeBSD" we will mean the BSD derivative UNIX-like operating system FreeBSD, whereas when saying "the FreeBSD Project" we will mean the project organisation. [[model-orgstruct]] == Organisational structure While no-one takes ownership of FreeBSD, the FreeBSD organisation is divided into core, committers and contributors and is part of the FreeBSD community that lives around it. The FreeBSD Project's structure (in order of descending authority) [.informaltable] [cols="1,1", options="header"] |=== | Group | Number of people |Core members |9 |Committers |318 |Contributors |~3000 |=== Number of committers has been determined by going through CVS logs from January 1st, 2004 to December 31st, 2004 and contributors by going through the list of contributions and problem reports. The main resource in the FreeBSD community is its developers: the committers and contributors. It is with their contributions that the project can move forward. Regular developers are referred to as contributors. As of January 1st, 2003, there are an estimated 5500 contributors on the project. Committers are developers with the privilege of being able to commit changes. These are usually the most active developers who are willing to spend their time not only integrating their own code but integrating code submitted by the developers who do not have this privilege. They are also the developers who elect the core team, and they have access to closed discussions. The project can be grouped into four distinct separate parts, and most developers will focus their involvement in one part of FreeBSD. The four parts are kernel development, userland development, ports and documentation. When referring to the base system, both kernel and userland is meant. This split changes our table to look like this: The FreeBSD Project's structure with committers in categories [.informaltable] [cols="1,1,1", options="header"] |=== | Group | Category | Number of people |Core members | |9 |Committers |Base |164 | |Docs |45 | |Ports |166 | |Total |374 |Contributors | |~3000 |=== Number of committers per area has been determined by going through CVS logs from January 1st, 2004 to December 31st, 2004. Note that many committers work in multiple areas, making the total number higher than the real number of committers. The total number of active unique committers on June 2022 was 317. Committers fall into three groups: committers who are only concerned with one area of the project (for instance file systems), committers who are involved only with one sub-project, and committers who commit to different parts of the code, including sub-projects. Because some committers work on different parts, the total number in the committers section of the table is higher than in the above table. The kernel is the main building block of FreeBSD. While the userland applications are protected against faults in other userland applications, the entire system is vulnerable to errors in the kernel. This, combined with the vast amount of dependencies in the kernel and that it is not easy to see all the consequences of a kernel change, demands developers with a relative full understanding of the kernel. Multiple development efforts in the kernel also require a closer coordination than userland applications do. The core utilities, known as userland, provide the interface that identifies FreeBSD, both user interface, shared libraries and external interfaces to connecting clients. Currently, 162 people are involved in userland development and maintenance, many being maintainers for their own part of the code. Maintainership will be discussed in the crossref:dev-model[role-maintainer, Maintainership] section. Documentation is handled by crossref:dev-model[sub-project-documentation, The FreeBSD Documentation Project] and includes all documents surrounding the FreeBSD project, including the web pages. There were during 2004 101 people making commits to the FreeBSD Documentation Project. Ports is the collection of meta-data that is needed to make software packages build correctly on FreeBSD. An example of a port is the port for the web-browser Mozilla. It contains information about where to fetch the source, what patches to apply and how, and how the package should be installed on the system. This allows automated tools to fetch, build and install the package. As of this writing, there are more than 12600 ports available. footnote:[Statistics are generated by counting the number of entries in the file fetched by portsdb by April 1st, 2005. portsdb is a part of the port sysutils/portupgrade.] , ranging from web servers to games, programming languages and most of the application types that are in use on modern computers. Ports will be discussed further in the section crossref:dev-model[sub-project-ports, The Ports Subproject]. [[methodology-model]] == Methodology model [[development-model]] === Development model There is no defined model for how people write code in FreeBSD. However, Niels Jørgenssen has suggested a model of how written code is integrated into the project. Jørgenssen's model for change integration [.informaltable] [cols="1,1,1", options="header"] |=== | Stage | Next if successful | Next if unsuccessful |code |review | |review |pre-commit test |code |pre-commit test |development release |code |development release |parallel debugging |code |parallel debugging |production release |code |production release | |code |=== The "development release" is the FreeBSD-CURRENT ("-CURRENT") branch and the -"production release" is the FreeBSD-STABLE branch ("-STABLE") [crossref:dev-model[jorgensen2001, Jørgensen, 2001]]. +"production release" is the FreeBSD-STABLE branch ("-STABLE") [ crossref:dev-model[jorgensen2001, Jørgensen 2001] ]. This is a model for one change, and shows that after coding, developers seek community review and try integrating it with their own systems. After integrating the change into the development release, called FreeBSD-CURRENT, it is tested by many users and developers in the FreeBSD community. After it has gone through enough testing, it is merged into the production release, called FreeBSD-STABLE. Unless each stage is finished successfully, the developer needs to go back and make modifications in the code and restart the process. To integrate a change with either -CURRENT or -STABLE is called making a commit. Jørgensen found that most FreeBSD developers work individually, meaning that this model is used in parallel by many developers on the different ongoing development efforts. A developer can also be working on multiple changes, so that while they are waiting for review or people to test one or more of their changes, they may be writing another change. As each commit represents an increment, this is a massively incremental model. The commits are in fact so frequent that during one year footnote:[The period from January 1st, 2004 to December 31st, 2004 was examined to find this number.] , 85427 commits were made, making a daily average of 233 commits. Within the "code" bracket in Jørgensen's model, each programmer has their own working style and follows their own development models. The bracket could very well have been called "development" as it includes requirements gathering and analysis, system and detailed design, implementation and verification. However, the only output from these stages is the source code or system documentation. From a stepwise model's perspective (such as the waterfall model), the other brackets can be seen as further verification and system integration. This system integration is also important to see if a change is accepted by the community. Up until the code is committed, the developer is free to choose how much to communicate about it to the rest of the project. In order for -CURRENT to work as a buffer (so that bright ideas that had some undiscovered drawbacks can be backed out) the minimum time a commit should be in -CURRENT before merging it to -STABLE is 3 days. Such a merge is referred to as an MFC (Merge From Current). It is important to notice the word "change". Most commits do not contain radical new features, but are maintenance updates. The only exceptions from this model are security fixes and changes to features that are deprecated in the -CURRENT branch. In these cases, changes can be committed directly to the -STABLE branch. In addition to many people working on the project, there are many related projects to the FreeBSD Project. These are either projects developing brand new features, sub-projects or projects whose outcome is incorporated into FreeBSD footnote:[For instance, the development of the Bluetooth stack started as a sub-project until it was deemed stable enough to be merged into the -CURRENT branch. Now it is a part of the core FreeBSD system.]. These projects fit into the FreeBSD Project just like regular development efforts: they produce code that is integrated with the FreeBSD Project. However, some of them (like Ports and Documentation) have the privilege of being applicable to both branches or commit directly to both -CURRENT and -STABLE. There is no standards to how design should be done, nor is design collected in a centralised repository. The main design is that of 4.4BSD. footnote:[According to Kirk McKusick, after 20 years of developing UNIX operating systems, the interfaces are for the most part figured out. There is therefore no need for much design. However, new applications of the system and new hardware leads to some implementations being more beneficial than those that used to be preferred. One example is the introduction of web browsing that made the normal TCP/IP connection a short burst of data rather than a steady stream over a longer period of time.] As design is a part of the "Code" bracket in Jørgenssen's model, it is up to every developer or sub-project how this should be done. Even if the design should be stored in a central repository, the output from the design stages would be of limited use as the differences of methodologies would make them poorly if at all interoperable. For the overall design of the project, the project relies on the sub-projects to negotiate fit interfaces between each other rather than to dictate interfacing. [[release-branches]] === Release branches The releases of FreeBSD are best illustrated by a tree with many branches where each major branch represents a major version. Minor versions are represented by branches of the major branches. In the following release tree, arrows that follow one-another in a particular direction represent a branch. Boxes with full lines and diamonds represent official releases. Boxes with dotted lines represent the development branch at that time. Security branches are represented by ovals. Diamonds differ from boxes in that they represent a fork, meaning a place where a branch splits into two branches where one of the branches becomes a sub-branch. For example, at 4.0-RELEASE the 4.0-CURRENT branch split into 4-STABLE and 5.0-CURRENT. At 4.5-RELEASE, the branch forked off a security branch called RELENG_4_5. .The FreeBSD release tree image::branches.png[Refer to table below for a screen-reader friendly version.] [.informaltable] [cols="1,1,1", options="header"] |=== | Major release | Forked from | Following minor releases |... | | |3.0 Current (development branch) | |Releng 3 branches: 3.0 Release to 3.5 Release, leading to 3.5.1 Release and the subsequent 3 Stable branch |4.0 Current (development branch) |3.1 Release |Releng 4 branches: 4.1 Release to 4.6 Release (and 4.6.2 Release), then 4.7 Release to 4.11 Release (all starting at 4.3 Release also leading to a Releng_4_n branch), and the subsequent 4 Release branch |5.0 Current (development branch) |4.0 Release |Releng 5 branches: 5.0 Release to 5.4 Release (all except 5.0 and 5.3 also leading to a Releng_5_n branch), and the subsequent 5 Release branch |6.0 Current (development branch) |5.3 Release | |... | | |=== The latest -CURRENT version is always referred to as -CURRENT, while the latest -STABLE release is always referred to as -STABLE. -In this figure, -STABLE refers to 4-STABLE while -CURRENT refers to 5.0-CURRENT following 5.0-RELEASE. [crossref:dev-model[freebsd-releng, FreeBSD, 2002E]] +In this figure, -STABLE refers to 4-STABLE while -CURRENT refers to 5.0-CURRENT following 5.0-RELEASE. [ crossref:dev-model[freebsd-releng, FreeBSD 2002E] ] A "major release" is always made from the -CURRENT branch. However, the -CURRENT branch does not need to fork at that point in time, but can focus on stabilising. An example of this is that following 3.0-RELEASE, 3.1-RELEASE was also a continuation of the -CURRENT-branch, and -CURRENT did not become a true development branch until this version was released and the 3-STABLE branch was forked. When -CURRENT returns to becoming a development branch, it can only be followed by a major release. 5-STABLE is predicted to be forked off 5.0-CURRENT at around 5.3-RELEASE. It is not until 5-STABLE is forked that the development branch will be branded 6.0-CURRENT. A "minor release" is made from the -CURRENT branch following a major release, or from the -STABLE branch. Following and including, 4.3-RELEASEfootnote:[The first release this actually happened for was 4.5-RELEASE, but security branches were at the same time created for 4.3-RELEASE and 4.4-RELEASE.], when a minor release has been made, it becomes a "security branch". This is meant for organisations that do not want to follow the -STABLE branch and the potential new/changed features it offers, but instead require an absolutely stable environment, only updating to implement security updates. footnote:[There is a terminology overlap with respect to the word "stable", which leads to some confusion. The -STABLE branch is still a development branch, whose goal is to be useful for most people. If it is never acceptable for a system to get changes that are not announced at the time it is deployed, that system should run a security branch.] Each update to a security branch is called a "patchlevel". For every security enhancement that is done, the patchlevel number is increased, making it easy for people tracking the branch to see what security enhancements they have implemented. In cases where there have been especially serious security flaws, an entire new release can be made from a security branch. An example of this is 4.6.2-RELEASE. [[model-summary]] === Model summary To summarise, the development model of FreeBSD can be seen as the following tree: .The overall development model image::freebsd-code-model.png[Refer to paragraphs below for a screen-reader friendly version.] The tree of the FreeBSD development with ongoing development efforts and continuous integration. The tree symbolises the release versions with major versions spawning new main branches and minor versions being versions of the main branch. The top branch is the -CURRENT branch where all new development is integrated, and the -STABLE branch is the branch directly below it. Below the -STABLE branch are old, unsupported versions. Clouds of development efforts hang over the project where developers use the development models they see fit. The product of their work is then integrated into -CURRENT where it undergoes parallel debugging and is finally merged from -CURRENT into -STABLE. Security fixes are merged from -STABLE to the security branches. Many committers have a special area of responsibility. These roles are called hats. These hats can be either project roles, such as public relations officer, or maintainer for a certain area of the code. Because this is a project where people give voluntarily of their spare time, people with assigned hats are not always available. They must therefore appoint a deputy that can perform the hat's role in their absence. The other option is to have the role held by a group. Many of these hats are not formalised. Formalised hats have a charter stating the exact purpose of the hat along with its privileges and responsibilities. The writing of such charters is a new part of the project, and has thus yet to be completed for all hats. These hat descriptions are not such a formalisation, rather a summary of the role with links to the charter where available and contact addresses. [[sect-hats]] == Hats [[general-hats]] === General Hats [[role-contributor]] ==== Contributor A Contributor contributes to the FreeBSD project either as a developer, as an author, by sending problem reports, or in other ways contributing to the -progress of the project. A contributor has no special privileges in the FreeBSD project. [crossref:dev-model[freebsd-contributors, FreeBSD, 2002F]] +progress of the project. A contributor has no special privileges in the FreeBSD project. [ crossref:dev-model[freebsd-contributors, FreeBSD 2002F] ] [[role-committer]] ==== Committer A person who has the required privileges to add their code or documentation to the repository. A committer has made a commit within the past 12 months. -[crossref:dev-model[freebsd-developer-handbook, FreeBSD, 2000A]] An active committer is a committer who has made an average of one commit per month during that time. +[ crossref:dev-model[freebsd-developer-handbook, FreeBSD 2000A] ] An active committer is a committer who has made an average of one commit per month during that time. It is worth noting that there are no technical barriers to prevent someone, once having gained commit privileges to the main- or a sub-project, to make commits in parts of that project's source the committer did not specifically get permission to modify. However, when wanting to make modifications to parts a committer has not been involved in before, they should read the logs to see what has happened in this area before, and also read the MAINTAINERS file to see if the maintainer of this part has any special requests on how changes in the code should be made. [[role-core]] ==== Core Team The core team is elected by the committers from the pool of committers and serves as the board of directors of the FreeBSD project. It promotes active contributors to committers, assigns people to well-defined hats, and is the final arbiter of decisions involving which way the project should be heading. As of July 1st, 2004, core consisted of 9 members. Elections are held every two years. [[role-maintainer]] ==== Maintainership Maintainership means that the person is responsible for what is allowed to go into that area of the code and has the final say should disagreements over the code occur. This involves proactive work aimed at stimulating contributions and reactive work in reviewing commits. With the FreeBSD source comes the MAINTAINERS file that contains a one-line summary of how each maintainer would like contributions to be made. Having this notice and contact information enables developers to focus on the development effort rather than being stuck in a slow correspondence should the maintainer be unavailable for some time. If the maintainer is unavailable for an unreasonably long period of time, and other people do a significant amount of work, maintainership may be switched without the maintainer's approval. This is based on the stance that maintainership should be demonstrated, not declared. Maintainership of a particular piece of code is a hat that is not held as a group. [[official-hats]] === Official Hats The official hats in the FreeBSD Project are hats that are more or less formalised and mainly administrative roles. They have the authority and responsibility for their area. The following list shows the responsibility lines and gives a description of each hat, including who it is held by. [[role-doc-manager]] ==== Documentation project manager crossref:dev-model[sub-project-documentation, The FreeBSD Documentation Project] architect is responsible for defining and following up documentation goals for the committers in the Documentation project, which they supervise. Hat held by: The DocEng team mailto:doceng@FreeBSD.org[doceng@FreeBSD.org]. The https://www.freebsd.org/internal/doceng/[ DocEng Charter]. [[role-postmaster]] ==== Postmaster The Postmaster is responsible for mail being correctly delivered to the committers' email address. They are also responsible for ensuring that the mailing lists work and should take measures against possible disruptions of mail such as having troll-, spam- and virus-filters. Hat currently held by: the Postmaster Team mailto:postmaster@FreeBSD.org[postmaster@FreeBSD.org]. [[role-release-coordination]] ==== Release Coordination The responsibilities of the Release Engineering Team are * Setting, publishing and following a release schedule for official releases * Documenting and formalising release engineering procedures * Creation and maintenance of code branches * Coordinating with the Ports and Documentation teams to have an updated set of packages and documentation released with the new releases * Coordinating with the Security team so that pending releases are not affected by recently disclosed vulnerabilities. Further information about the development process is available in the crossref:dev-model[process-release-engineering, Release engineering] section. [[role-releng]] Hat held by: the Release Engineering team mailto:re@FreeBSD.org[re@FreeBSD.org]. The https://www.freebsd.org/releng/charter/[ Release Engineering Charter]. [[role-pr-cr]] ==== Public Relations & Corporate Liaison The Public Relations & Corporate Liaison's responsibilities are: * Making press statements when happenings that are important to the FreeBSD Project happen. * Being the official contact person for corporations that are working close with the FreeBSD Project. * Take steps to promote FreeBSD within both the Open Source community and the corporate world. * Handle the "freebsd-advocacy" mailing list. This hat is currently not occupied. [[role-security-officer]] ==== Security Officer The Security Officer's main responsibility is to coordinate information exchange with others in the security community and in the FreeBSD project. The Security Officer is also responsible for taking action when security problems are reported and promoting proactive development behavior when it comes to security. Because of the fear that information about vulnerabilities may leak out to people with malicious intent before a patch is available, only the Security Officer, consisting of an officer, a deputy and two crossref:dev-model[role-core, Core Team] members, receive sensitive information about security issues. However, to create or implement a patch, the Security Officer has the Security Officer Team mailto:security-team@FreeBSD.org[security-team@FreeBSD.org] to help do the work. [[role-repo-manager]] ==== Source Repository Manager The Source Repository Manager is the only one who is allowed to directly modify the repository without using the crossref:dev-model[tool-git, Git] tool. It is their responsibility to ensure that technical problems that arise in the repository are resolved quickly. The source repository manager has the authority to back out commits if this is necessary to resolve a Git technical problem. Hat held by: the Source Repository Manager mailto:clusteradm@FreeBSD.org[clusteradm@FreeBSD.org]. [[role-election-manager]] ==== Election Manager The Election Manager is responsible for the crossref:dev-model[process-core-election, Core election] process. The manager is responsible for running and maintaining the election system, and is the final authority should minor unforeseen events happen in the election process. Major unforeseen events have to be discussed with the crossref:dev-model[role-core, Core Team] Hat held only during elections. [[role-webmaster]] ==== Web site Management The Web site Management hat is responsible for coordinating the rollout of updated web pages on mirrors around the world, for the overall structure of the primary web site and the system it is running upon. The management needs to coordinate the content with crossref:dev-model[sub-project-documentation, The FreeBSD Documentation Project] and acts as maintainer for the "www" tree. Hat held by: the FreeBSD Webmasters mailto:www@FreeBSD.org[www@FreeBSD.org]. [[role-ports-manager]] ==== Ports Manager The Ports Manager acts as a liaison between crossref:dev-model[sub-project-ports, The Ports Subproject] and the core project, and all requests from the project should go to the ports manager. Hat held by: the Ports Management Team mailto:portmgr@FreeBSD.org[portmgr@FreeBSD.org]. The https://www.freebsd.org/portmgr/charter/[Portmgr charter]. [[role-standards]] ==== Standards The Standards hat is responsible for ensuring that FreeBSD complies with the standards it is committed to , keeping up to date on the development of these standards and notifying FreeBSD developers of important changes that allows them to take a proactive role and decrease the time between a standards update and FreeBSD's compliancy. Hat currently held by: Garrett Wollman mailto:wollman@FreeBSD.org[wollman@FreeBSD.org]. [[role-core-secretary]] ==== Core Secretary The Core Secretary's main responsibility is to write drafts to and publish the final Core Reports. The secretary also keeps the core agenda, thus ensuring that no balls are dropped unresolved. Hat currently held by: {rene}. [[role-bugmeister]] ==== Bugmeister The Bugmeister is responsible for ensuring that the maintenance database is in working order, that the entries are correctly categorised and that there are no invalid entries. They supervise bugbusters. Hat currently held by: the Bugmeister Team mailto:bugmeister@FreeBSD.org[bugmeister@FreeBSD.org]. [[role-donations]] ==== Donations Liaison Officer The task of the donations liaison officer is to match the developers with needs with people or organisations willing to make a donation. Hat held by: the Donations Liaison Office mailto:donations@FreeBSD.org[donations@FreeBSD.org]. The https://www.freebsd.org/donations/[ Donations Liaison Charter]. [[role-admin]] ==== Admin (Also called "FreeBSD Cluster Admin") The admin team consists of the people responsible for administrating the computers that the project relies on for its distributed work and communication to be synchronised. It consists mainly of those people who have physical access to the servers. Hat held by: the Admin team mailto:admin@FreeBSD.org[admin@FreeBSD.org]. [[proc-depend-hats]] === Process dependent hats [[role-problem-originator]] ==== Report originator The person originally responsible for filing a Problem Report. [[role-bugbuster]] ==== Bugbuster A person who will either find the right person to solve the problem, or close the PR if it is a duplicate or otherwise not an interesting one. [[role-mentor]] ==== Mentor A mentor is a committer who takes it upon them to introduce a new committer to the project, both in terms of ensuring the new committer's setup is valid, that the new committer knows the available tools required in their work, and that the new committer knows what is expected of them in terms of behavior. [[role-vendor]] ==== Vendor The person(s) or organisation whom external code comes from and whom patches are sent to. [[role-reviewer]] ==== Reviewers People on the mailing list where the request for review is posted. The following section will describe the defined project processes. Issues that are not handled by these processes happen on an ad-hoc basis based on what has been customary to do in similar cases. [[model-processes]] == Processes [[proc-addrem-committer]] === Adding new and removing old committers The Core team has the responsibility of giving and removing commit privileges to contributors. This can only be done through a vote on the core mailing list. The ports and documentation sub-projects can give commit privileges to people working on these projects, but have to date not removed such privileges. Normally a contributor is recommended to core by a committer. For contributors or outsiders to contact core asking to be a committer is not well thought of and is usually rejected. If the area of particular interest for the developer potentially overlaps with other committers' area of maintainership, the opinion of those maintainers is sought. However, it is frequently this committer that recommends the developer. When a contributor is given committer status, they are assigned a mentor. The committer who recommended the new committer will, in the general case, take it upon themselves to be the new committers mentor. When a contributor is given their commit bit, a crossref:dev-model[tool-pgp, Pretty Good Privacy]-signed email is sent from either crossref:dev-model[role-core-secretary, Core Secretary], crossref:dev-model[role-ports-manager, Ports Manager], or nik@freebsd.org to both admins@freebsd.org, the assigned mentor, the new committer, and core confirming the approval of a new account. The mentor then gathers a password line, crossref:dev-model[tool-ssh2, Secure Shell] public key, and PGP key from the new committer and sends them to crossref:dev-model[role-admin, Admin]. When the new account is created, the mentor activates the commit bit and guides the new committer through the rest of the initial process. .Process summary: adding a new committer image::proc-add-committer.png[Refer to paragraph below for a screen-reader friendly version.] When a contributor sends a piece of code, the receiving committer may choose to recommend that the contributor is given commit privileges. If they recommend this to core, core will vote on this recommendation. If the vote is in favour, a mentor is assigned the new committer and the new committer has to email their details to the administrators for an account to be created. After this, the new committer is all set to make their first commit. By tradition, this is by adding their name to the committers list. Recall that a committer is considered to be someone who has committed code during the past 12 months. However, it is not until after 18 months of inactivity have passed that commit privileges are eligible to be revoked. -[crossref:dev-model[freebsd-expiration-policy, FreeBSD, 2002H]] +[ crossref:dev-model[freebsd-expiration-policy, FreeBSD 2002H] ] There are, however, no automatic procedures for doing this. For reactions concerning commit privileges not triggered by time, see crossref:dev-model[process-reactions,section 1.5.8]. .Process summary: removing a committer image::proc-rm-committer.png[Refer to paragraph below for a screen-reader friendly version.] When Core decides to clean up the committers list, they check who has not made a commit for the past 18 months. Committers who have not done so have their commit bits revoked and their account removed by the administrators. It is also possible for committers to request that their commit bit be retired if for some reason they are no longer going to be actively committing to the project. In this case, it can also be restored at a later time by core, should the committer ask. Roles in this process: . crossref:dev-model[role-core, Core Team] . crossref:dev-model[role-contributor, Contributor] . crossref:dev-model[role-committer, Committer] . crossref:dev-model[role-maintainer, Maintainership] . crossref:dev-model[role-mentor, Mentor] -[crossref:dev-model[freebsd-bylaws, FreeBSD, 2000A]] -[crossref:dev-model[freebsd-expiration-policy, FreeBSD, 2002H]] -[crossref:dev-model[freebsd-new-account, FreeBSD, 2002I]] +[ crossref:dev-model[freebsd-bylaws, FreeBSD 2000A] ] +[ crossref:dev-model[freebsd-expiration-policy, FreeBSD 2002H] ] +[ crossref:dev-model[freebsd-new-account, FreeBSD 2002I] ] [[committing]] === Committing code The committing of new or modified code is one of the most frequent processes in the FreeBSD project and will usually happen many times a day. Committing of code can only be done by a "committer". Committers commit either code written by themselves, code submitted to them, or code submitted through a crossref:dev-model[model-pr,problem report]. When code is written by the developer that is non-trivial, they should seek a code review from the community. This is done by sending mail to the relevant list asking for review. Before submitting the code for review, they should ensure it compiles correctly with the entire tree and that all relevant tests run. This is called "pre-commit test". When contributed code is received, it should be reviewed by the committer and tested the same way. When a change is committed to a part of the source that has been contributed from an outside crossref:dev-model[role-vendor, Vendor], the maintainer should ensure that the patch is contributed back to the vendor. This is in line with the open source philosophy and makes it easier to stay in sync with outside projects as the patches do not have to be reapplied every time a new release is made. After the code has been available for review and no further changes are necessary, the code is committed into the development branch, -CURRENT. If the change applies for the -STABLE branch or the other branches as well, a "Merge From Current" ("MFC") countdown is set by the committer. After the number of days the committer chose when setting the MFC have passed, an email will automatically be sent to the committer reminding them to commit it to the -STABLE branch (and possibly security branches as well). Only security critical changes should be merged to security branches. Delaying the commit to -STABLE and other branches allows for "parallel debugging" where the committed code is tested on a wide range of configurations. This makes changes to -STABLE to contain fewer faults and thus giving the branch its name. .Process summary: A committer commits code image::proc-commit.png[Refer to paragraph below for a screen-reader friendly version.] When a committer has written a piece of code and wants to commit it, they first need to determine if it is trivial enough to go in without prior review or if it should first be reviewed by the developer community. If the code is trivial or has been reviewed and the committer is not the maintainer, they should consult the maintainer before proceeding. If the code is contributed by an outside vendor, the maintainer should create a patch that is sent back to the vendor. The code is then committed and then deployed by the users. Should they find problems with the code, this will be reported and the committer can go back to writing a patch. If a vendor is affected, they can choose to implement or ignore the patch. .Process summary: A contributor commits code image::proc-contrib.png[Refer to paragraphs below and above for a screen-reader friendly version.] The difference when a contributor makes a code contribution is that they submit the code through the Bugzilla interface. This report is picked up by the maintainer who reviews the code and commits it. Hats included in this process are: . crossref:dev-model[role-committer, Committer] . crossref:dev-model[role-contributor, Contributor] . crossref:dev-model[role-vendor, Vendor] . crossref:dev-model[role-reviewer, Reviewers] -[crossref:dev-model[freebsd-committer, FreeBSD, 2001]] -[crossref:dev-model[jorgensen2001, Jørgensen, 2001]] +[ crossref:dev-model[freebsd-committer, FreeBSD 2001] ] +[ crossref:dev-model[jorgensen2001, Jørgensen 2001] ] [[process-core-election]] === Core election Core elections are held at least every two years. footnote:[The first Core election was held September 2000] Nine core members are elected. New elections are held if the number of core members drops below seven. New elections can also be held should at least 1/3 of the active committers demand this. When an election is to take place, core announces this at least 6 weeks in advance, and appoints an election manager to run the elections. Only committers can be elected into core. The candidates need to submit their candidacy at least one week before the election starts, but can refine their statements until the voting starts. They are presented in the http://election.uk.freebsd.org/candidates.html[candidates list]. When writing their election statements, the candidates must answer a few standard questions submitted by the election manager. During elections, the rule that a committer must have committed during the 12 past months is followed strictly. Only these committers are eligible to vote. When voting, the committer may vote once in support of up to nine nominees. The voting is done over a period of four weeks with reminders being posted on "developers" mailing list that is available to all committers. The election results are released one week after the election ends, and the new core team takes office one week after the results have been posted. Should there be a voting tie, this will be resolved by the new, unambiguously elected core members. Votes and candidate statements are archived, but the archives are not publicly available. .Process summary: Core elections image::proc-elections.png[Refer to paragraph below for a screen-reader friendly version.] Core announces the election and selects an election manager who prepares the elections, and when ready, candidates can announce their candidacies through submitting their statements. The committers then vote. After the vote is over, the election results are announced and the new core team takes office. Hats in core elections are: * crossref:dev-model[role-core, Core Team] * crossref:dev-model[role-committer, Committer] * crossref:dev-model[role-election-manager, Election Manager] -[crossref:dev-model[freebsd-bylaws, FreeBSD, 2000A]] -[crossref:dev-model[bsd-election2002, FreeBSD, 2002B]] -[crossref:dev-model[freebsd-election, FreeBSD, 2002G]] +[ crossref:dev-model[freebsd-bylaws, FreeBSD 2000A] ] +[ crossref:dev-model[bsd-election2002, FreeBSD 2002B] ] +[ crossref:dev-model[freebsd-election, FreeBSD 2002G] ] [[new-features]] === Development of new features Within the project there are sub-projects that are working on new features. These projects are generally done by one person -[crossref:dev-model[jorgensen2001, Jørgensen, 2001]]. +[ crossref:dev-model[jorgensen2001, Jørgensen 2001] ]. Every project is free to organise development as it sees fit. However, when the project is merged to the -CURRENT branch it must follow the project guidelines. When the code has been well tested in the -CURRENT branch and deemed stable enough and relevant to the -STABLE branch, it is merged to the -STABLE branch. The requirements of the project are given by developer wishes, requests from the community in terms of direct requests by mail, Problem Reports, commercial funding for the development of features, or contributions by the scientific community. The wishes that come within the responsibility of a developer are given to that developer who prioritises their time between the request and their wishes. A common way to do this is maintain a TODO-list maintained by the project. Items that do not come within someone's responsibility are collected on TODO-lists unless someone volunteers to take the responsibility. All requests, their distribution and follow-up are handled by the crossref:dev-model[tool-bugzilla, Bugzilla] tool. Requirements analysis happens in two ways. The requests that come in are discussed on mailing lists, both within the main project and in the sub-project that the request belongs to or is spawned by the request. Furthermore, individual developers on the sub-project will evaluate the feasibility of the requests and determine the prioritisation between them. Other than archives of the discussions that have taken place, no outcome is created by this phase that is merged into the main project. As the requests are prioritised by the individual developers on the basis of doing what they find interesting, necessary, or are funded to do, there is no overall strategy or prioritisation of what requests to regard as requirements and following up their correct implementation. However, most developers have some shared vision of what issues are more important, and they can ask for guidelines from the release engineering team. The verification phase of the project is two-fold. Before committing code to the current-branch, developers request their code to be reviewed by their peers. This review is for the most part done by functional testing, but also code review is important. When the code is committed to the branch, a broader functional testing will happen, that may trigger further code review and debugging should the code not behave as expected. This second verification form may be regarded as structural verification. Although the sub-projects themselves may write formal tests such as unit tests, these are usually not collected by the main project and are usually removed before the code is committed to the current branch. footnote:[More and more tests are however performed when building the system (make world). These tests are however a very new addition and no systematic framework for these tests have yet been created.] [[model-maintenance]] === Maintenance It is an advantage to the project to for each area of the source have at least one person that knows this area well. Some parts of the code have designated maintainers. Others have de-facto maintainers, and some parts of the system do not have maintainers. The maintainer is usually a person from the sub-project that wrote and integrated the code, or someone who has ported it from the platform it was written for. footnote:[sendmail and named are examples of code that has been merged from other platforms.] The maintainer's job is to make sure the code is in sync with the project the code comes from if it is contributed code, and apply patches submitted by the community or write fixes to issues that are discovered. The main bulk of work that is put into the FreeBSD project is maintenance. -[crossref:dev-model[jorgensen2001, Jørgensen, 2001]] has made a figure showing the life cycle of changes. +[ crossref:dev-model[jorgensen2001, Jørgensen 2001] ] has made a figure showing the life cycle of changes. Jørgenssen's model for change integration [.informaltable] [cols="1,1,1", options="header"] |=== | Stage | Next if successful | Next if unsuccessful |code |review | |review |pre-commit test |code |pre-commit test |development release |code |development release |parallel debugging |code |parallel debugging |production release |code |production release | |code |=== Here "development release" refers to the -CURRENT branch while "production release" refers to the -STABLE branch. The "pre-commit test" is the functional testing by peer developers when asked to do so or trying out the code to determine the status of the sub-project. "Parallel debugging" is the functional testing that can trigger more review, and debugging when the code is included in the -CURRENT branch. As of this writing, there were 269 committers in the project. When they commit a change to a branch, that constitutes a new release. It is very common for users in the community to track a particular branch. The immediate existence of a new release makes the changes widely available right away and allows for rapid feedback from the community. This also gives the community the response time they expect on issues that are of importance to them. This makes the community more engaged, and thus allows for more and better feedback that again spurs more maintenance and ultimately should create a better product. Before making changes to code in parts of the tree that has a history unknown to the committer, the committer is required to read the commit logs to see why certain features are implemented the way they are in order not to make mistakes that have previously either been thought through or resolved. [[model-pr]] === Problem reporting Before FreeBSD 10, FreeBSD included a problem reporting tool called `send-pr`. Problems include bug reports, feature requests, feature enhancements and notices of new versions of external software that are included in the project. Although `send-pr` is available, users and developers are encouraged to submit issues using our https://bugs.freebsd.org/submit/[ problem report form]. Problem reports are sent to an email address where it is inserted into the Problem Reports maintenance database. A crossref:dev-model[role-bugbuster, Bugbuster] classifies the problem and sends it to the correct group or maintainer within the project. After someone has taken responsibility for the report, the report is being analysed. This analysis includes verifying the problem and thinking out a solution for the problem. Often feedback is required from the report originator or even from the FreeBSD community. Once a patch for the problem is made, the originator may be asked to try it out. Finally, the working patch is integrated into the project, and documented if applicable. It there goes through the regular maintenance cycle as described in section crossref:dev-model[model-maintenance, Maintenance]. These are the states a problem report can be in: open, analyzed, feedback, patched, suspended and closed. The suspended state is for when further progress is not possible due to the lack of information or for when the task would require so much work that nobody is working on it at the moment. .Process summary: problem reporting image::proc-pr.png[Refer to paragraph below for a screen-reader friendly version.] A problem is reported by the report originator. It is then classified by a bugbuster and handed to the correct maintainer. They verify the problem and discuss the problem with the originator until they have enough information to create a working patch. This patch is then committed and the problem report is closed. The roles included in this process are: . crossref:dev-model[role-problem-originator, Report originator] . crossref:dev-model[role-maintainer, Maintainership] . crossref:dev-model[role-bugbuster, Bugbuster] -[crossref:dev-model[freebsd-handle-pr, FreeBSD, 2002C]]. -[crossref:dev-model[freebsd-send-pr, FreeBSD, 2002D]] +[ crossref:dev-model[freebsd-handle-pr, FreeBSD 2002C] ]. +[ crossref:dev-model[freebsd-send-pr, FreeBSD 2002D] ]. [[process-reactions]] === Reacting to misbehavior -[crossref:dev-model[freebsd-committer, FreeBSD, 2001]] has a number of rules that committers should follow. +[ crossref:dev-model[freebsd-committer, FreeBSD 2001] ] has a number of rules that committers should follow. However, it happens that these rules are broken. The following rules exist in order to be able to react to misbehavior. They specify what actions will result in how long a suspension of the committer's commit privileges. * Committing during code freezes without the approval of the Release Engineering team - 2 days * Committing to a security branch without approval - 2 days * Commit wars - 5 days to all participating parties * Impolite or inappropriate behavior - 5 days -[crossref:dev-model[ref-freebsd-trenches, Lehey, 2002]] +[ crossref:dev-model[ref-freebsd-trenches, Lehey 2002] ] For the suspensions to be efficient, any single core member can implement a suspension before discussing it on the "core" mailing list. Repeat offenders can, with a 2/3 vote by core, receive harsher penalties, including permanent removal of commit privileges. (However, the latter is always viewed as a last resort, due to its inherent tendency to create controversy.) All suspensions are posted to the "developers" mailing list, a list available to committers only. It is important that you cannot be suspended for making technical errors. All penalties come from breaking social etiquette. Hats involved in this process: * crossref:dev-model[role-core, Core Team] * crossref:dev-model[role-committer, Committer] [[process-release-engineering]] === Release engineering The FreeBSD project has a Release Engineering team with a principal release engineer that is responsible for creating releases of FreeBSD that can be brought out to the user community via the net or sold in retail outlets. Since FreeBSD is available on multiple platforms and releases for the different architectures are made available at the same time, the team has one person in charge of each architecture. Also, there are roles in the team responsible for coordinating quality assurance efforts, building a package set and for having an updated set of documents. When referring to the release engineer, a representative for the release engineering team is meant. When a release is coming, the FreeBSD project changes shape somewhat. A release schedule is made containing feature- and code-freezes, release of interim releases and the final release. A feature-freeze means no new features are allowed to be committed to the branch without the release engineers' explicit consent. Code-freeze means no changes to the code (like bugs-fixes) are allowed to be committed without the release engineers' explicit consent. This feature- and code-freeze is known as stabilising. During the release process, the release engineer has the full authority to revert to older versions of code and thus "back out" changes should they find that the changes are not suitable to be included in the release. There are three different kinds of releases: . .0 releases are the first release of a major version. These are branched of the -CURRENT branch and have a significantly longer release engineering cycle due to the unstable nature of the -CURRENT branch . .X releases are releases of the -STABLE branch. They are scheduled to come out every 4 months. . .X.Y releases are security releases that follow the .X branch. These come out only when sufficient security fixes have been merged since the last release on that branch. New features are rarely included, and the security team is far more involved in these than in regular releases. For releases of the -STABLE-branch, the release process starts 45 days before the anticipated release date. During the first phase, the first 15 days, the developers merge what changes they have had in -CURRENT that they want to have in the release to the release branch. When this period is over, the code enters a 15 day code freeze in which only bug fixes, documentation updates, security-related fixes and minor device driver changes are allowed. These changes must be approved by the release engineer in advance. At the beginning of the last 15 day period a release candidate is created for widespread testing. Updates are less likely to be allowed during this period, except for important bug fixes and security updates. In this final period, all releases are considered release candidates. At the end of the release process, a release is created with the new version number, including binary distributions on web sites and the creation of CD-ROM images. However, the release is not considered "really released" until a crossref:dev-model[tool-pgp, Pretty Good Privacy]-signed message stating exactly that, is sent to the mailing list freebsd-announce; anything labelled as a "release" before that may well be in-process and subject to change before the PGP-signed message is sent. footnote:[Many commercial vendors use these images to create CD-ROMs that are sold in retail outlets.]. The releases of the -CURRENT-branch (that is, all releases that end with ".0") are very similar, but with twice as long timeframe. It starts 8 weeks prior to the release with announcement of the release time line. Two weeks into the release process, the feature freeze is initiated and performance tweaks should be kept to a minimum. Four weeks prior to the release, an official beta version is made available. Two weeks prior to release, the code is officially branched into a new version. This version is given release candidate status, and as with the release engineering of -STABLE, the code freeze of the release candidate is hardened. However, development on the main development branch can continue. Other than these differences, the release engineering processes are alike. *.0 releases go into their own branch and are aimed mainly at early adopters. The branch then goes through a period of stabilisation, and it is not until the crossref:dev-model[role-releng, Release Engineering Team] decides the demands to stability have been satisfied that the branch becomes -STABLE and -CURRENT targets the next major version. While this for the majority has been with *.1 versions, this is not a demand. Most releases are made when a given date that has been deemed a long enough time since the previous release comes. A target is set for having major releases every 18 months and minor releases every 4 months. The user community has made it very clear that security and stability cannot be sacrificed by self-imposed deadlines and target release dates. For slips of time not to become too long with regards to security and stability issues, extra discipline is required when committing changes to -STABLE. . Make release schedule . Feature freeze . Code freeze . Make branch . Release candidate . Stabilize release (loop back to previous step as many times as necessary; when release is considered stable, proceed with next step) . Build packages . Warn mirrors . Publish release // Keep the spaces around the external square bracket to avoid a warning in the // PDF converter [ crossref:dev-model[freebsd-releng, FreeBSD, 2002E] ] [[tools]] == Tools The major support tools for supporting the development process are Bugzilla, Mailman, and OpenSSH. These are externally developed tools and are commonly used in the open source world. [[tool-git]] === Git Git is a system to handle multiple versions of text files and tracking who committed what changes and why. A project lives within a "repository" and different versions are considered different "branches". [[tool-bugzilla]] === Bugzilla Bugzilla is a maintenance database consisting of a set of tools to track bugs at a central site. It supports the bug tracking process for sending and handling bugs as well as querying and updating the database and editing bug reports. The project uses its web interface to send "Problem Reports" to the project's central Bugzilla server. The committers also have web and command-line clients. [[model-mailman]] === Mailman Mailman is a program that automates the management of mailing lists. The FreeBSD Project uses it to run 16 general lists, 60 technical lists, 4 limited lists and 5 lists with Git commit logs. It is also used for many mailing lists set up and used by other people and projects in the FreeBSD community. General lists are lists for the general public, technical lists are mainly for the development of specific areas of interest, and closed lists are for internal communication not intended for the general public. The majority of all the communication in the project goes through these 85 lists -[crossref:dev-model[ref-bsd-handbook, FreeBSD, 2003A], Appendix C]. +[ crossref:dev-model[ref-bsd-handbook, FreeBSD 2003A], Appendix C]. [[tool-pgp]] === Pretty Good Privacy Pretty Good Privacy, better known as PGP, is a cryptosystem using a public key architecture to allow people to digitally sign and/or encrypt information in order to ensure secure communication between two parties. A signature is used when sending information out to many recipients, enabling them to verify that the information has not been tampered with before they received it. In the FreeBSD Project this is the primary means of ensuring that information has been written by the person who claims to have written it, and not altered in transit. [[tool-ssh2]] === Secure Shell Secure Shell is a standard for securely logging into a remote system and for executing commands on the remote system. It allows other connections, called tunnels, to be established and protected between the two involved systems. This standard exists in two primary versions, and only version two is used for the FreeBSD Project. The most common implementation of the standard is OpenSSH that is a part of the project's main distribution. Since its source is updated more often than FreeBSD releases, the latest version is also available in the ports tree. [[sub-projects]] == Sub-projects Sub-projects are formed to reduce the amount of communication needed to coordinate the group of developers. When a problem area is sufficiently isolated, most communication would be within the group focusing on the problem, requiring less communication with the groups they communicate with than were the group not isolated. [[sub-project-ports]] === The Ports Subproject A "port" is a set of meta-data and patches that are needed to fetch, compile and install correctly an external piece of software on a FreeBSD system. The amount of ports has grown at a tremendous rate, as shown by the following figure. .Number of ports added between 1995 and 2022 [[fig-ports]] image::portsstatus.svg[Refer to paragraphs below for a screen-reader friendly version.] crossref:dev-model[fig-ports,image::portsstatus.svg] shows the number of ports available to FreeBSD in the period 1995 to 2022. It looks like the curve has first grown exponentially, and then from the middle of 2001 to the middle of 2007 grown linearly at a rate of about 2000 ports/year, before its growth rate gets lower. As the external software described by the port often is under continued development, the amount of work required to maintain the ports is already large, and increasing. This has led to the ports part of the FreeBSD project gaining a more empowered structure, and is more and more becoming a sub-project of the FreeBSD project. Ports has its own core team with the crossref:dev-model[role-ports-manager, Ports Manager] as its leader, and this team can appoint committers without FreeBSD Core's approval. Unlike in the FreeBSD Project, where a lot of maintenance frequently is rewarded with a commit bit, the ports sub-project contains many active maintainers that are not committers. Unlike the main project, the ports tree is not branched. Every release of FreeBSD follows the current ports collection and has thus available updated information on where to find programs and how to build them. This, however, means that a port that makes dependencies on the system may need to have variations depending on what version of FreeBSD it runs on. With an unbranched ports repository it is not possible to guarantee that any port will run on anything other than -CURRENT and -STABLE, in particular older, minor releases. There is neither the infrastructure nor volunteer time needed to guarantee this. For efficiency of communication, teams depending on Ports, such as the release engineering team, have their own ports liaisons. [[sub-project-documentation]] === The FreeBSD Documentation Project The FreeBSD Documentation project was started January 1995. From the initial group of a project leader, four team leaders and 16 members, they are now a total of 44 committers. The documentation mailing list has just under 300 members, indicating that there is quite a large community around it. The goal of the Documentation project is to provide good and useful documentation of the FreeBSD project, thus making it easier for new users to get familiar with the system and detailing advanced features for the users. The main tasks in the Documentation project are to work on current projects in the "FreeBSD Documentation Set", and translate the documentation to other languages. Like the FreeBSD Project, documentation is split in the same branches. This is done so that there is always an updated version of the documentation for each version. Only documentation errors are corrected in the security branches. Like the ports sub-project, the Documentation project can appoint documentation committers without FreeBSD Core's approval. -[crossref:dev-model[freebsd-doceng-charter, FreeBSD, 2003B]]. +[ crossref:dev-model[freebsd-doceng-charter, FreeBSD 2003B] ]. The Documentation project has extref:{fdp-primer}[a primer]. This is used both to introduce new project members to the standard tools and syntaxes and to act as a reference when working on the project. :sectnums!: [bibliography] [[bibliography]] == References [[brooks]] [Brooks, 1995] Frederick P. Brooks. Copyright © 1975, 1995 Pearson Education Limited. 0201835959. Addison-Wesley Pub Co. The Mythical Man-Month. Essays on Software Engineering, Anniversary Edition (2nd Edition). [[thesis]] [Saers, 2003] Niklas Saers. Copyright © 2003. A project model for the FreeBSD Project. Candidatus Scientiarum thesis. http://niklas.saers.com/thesis. [[jorgensen2001]] [Jørgensen, 2001] Niels Jørgensen. Copyright © 2001. Putting it All in the Trunk. Incremental Software Development in the FreeBSD Open Source Project. http://www.dat.ruc.dk/~nielsj/research/papers/freebsd.pdf. [[ref-pmbok]] [PMI, 2000] Project Management Institute. Copyright © 1996, 2000 Project Management Institute. 1-880410-23-0. Project Management Institute. Newtown Square Pennsylvania USA . PMBOK Guide. A Guide to the Project Management Body of Knowledge, 2000 Edition. [[freebsd-bylaws]] [FreeBSD, 2000A] Copyright © 2002 The FreeBSD Project. Core Bylaws. https://www.freebsd.org/internal/bylaws/. [[freebsd-developer-handbook]] [FreeBSD, 2002A] Copyright © 2002 The FreeBSD Documentation Project. FreeBSD Developer's Handbook. extref:{developers-handbook}[Developers Handbook]. [[bsd-election2002]] [FreeBSD, 2002B] Copyright © 2002 The FreeBSD Project. Core team election 2002. http://election.uk.freebsd.org/candidates.html. [[freebsd-handle-pr]] [FreeBSD, 2002C] Dag-Erling Smørgrav and Hiten Pandya. Copyright © 2002 The FreeBSD Documentation Project. The FreeBSD Documentation Project. Problem Report Handling Guidelines. extref:{pr-guidelines}[Problem Report Handling Guidelines]. [[freebsd-send-pr]] [FreeBSD, 2002D] Dag-Erling Smørgrav. Copyright © 2002 The FreeBSD Documentation Project. The FreeBSD Documentation Project. Writing FreeBSD Problem Reports. extref:{problem-reports}[Writing FreeBSD Problem Reports]. [[freebsd-committer]] [FreeBSD, 2001] Copyright © 2001 The FreeBSD Documentation Project. The FreeBSD Documentation Project. Committers Guide. extref:{committers-guide}[Committer's Guide]. [[freebsd-releng]] [FreeBSD, 2002E] Murray Stokely. Copyright © 2002 The FreeBSD Documentation Project. The FreeBSD Documentation Project. FreeBSD Release Engineering. extref:{releng}[FreeBSD Release Engineering]. [[ref-bsd-handbook]] [FreeBSD, 2003A] The FreeBSD Documentation Project. FreeBSD Handbook. extref:{handbook}[FreeBSD Handbook]. [[freebsd-contributors]] [FreeBSD, 2002F] Copyright © 2002 The FreeBSD Documentation Project. The FreeBSD Documentation Project. Contributors to FreeBSD. extref:{contributors}[Contributors to FreeBSD]. [[freebsd-election]] [FreeBSD, 2002G] Copyright © 2002 The FreeBSD Project. The FreeBSD Project. Core team elections 2002. http://election.uk.freebsd.org. [[freebsd-expiration-policy]] [FreeBSD, 2002H] Copyright © 2002 The FreeBSD Project. The FreeBSD Project. Commit Bit Expiration Policy. 2002/04/06 15:35:30. https://www.freebsd.org/internal/expire-bits/. [[freebsd-new-account]] [FreeBSD, 2002I] Copyright © 2002 The FreeBSD Project. The FreeBSD Project. New Account Creation Procedure. 2002/08/19 17:11:27. https://www.freebsd.org/internal/new-account/. [[freebsd-doceng-charter]] [FreeBSD, 2003B] Copyright © 2002 The FreeBSD Documentation Project. The FreeBSD Documentation Project. FreeBSD DocEng Team Charter. 2003/03/16 12:17. https://www.freebsd.org/internal/doceng/. [[ref-freebsd-trenches]] [Lehey, 2002] Greg Lehey. Copyright © 2002 Greg Lehey. Greg Lehey. Two years in the trenches. The evolution of a software project. http://www.lemis.com/grog/In-the-trenches.pdf. diff --git a/documentation/content/en/books/developers-handbook/secure/_index.adoc b/documentation/content/en/books/developers-handbook/secure/_index.adoc index fab08ad88b..bbcf991dec 100644 --- a/documentation/content/en/books/developers-handbook/secure/_index.adoc +++ b/documentation/content/en/books/developers-handbook/secure/_index.adoc @@ -1,289 +1,289 @@ --- title: Chapter 3. Secure Programming -authors: +authors: - author: Murray Stokely prev: books/developers-handbook/tools next: books/developers-handbook/l10n description: Secure Programming in FreeBSD tags: ["secure programming", "Buffer Overflows", "SetUID issues"] showBookMenu: true weight: 4 params: path: "/books/developers-handbook/secure/" --- [[secure]] = Secure Programming :doctype: book :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumoffset: 3 :partnums: :source-highlighter: rouge :experimental: :images-path: books/developers-handbook/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] [[secure-synopsis]] == Synopsis This chapter describes some of the security issues that have plagued UNIX(R) programmers for decades and some of the new tools available to help programmers avoid writing exploitable code. [[secure-philosophy]] == Secure Design Methodology Writing secure applications takes a very scrutinous and pessimistic outlook on life. Applications should be run with the principle of "least privilege" so that no process is ever running with more than the bare minimum access that it needs to accomplish its function. Previously tested code should be reused whenever possible to avoid common mistakes that others may have already fixed. One of the pitfalls of the UNIX(R) environment is how easy it is to make assumptions about the sanity of the environment. Applications should never trust user input (in all its forms), system resources, inter-process communication, or the timing of events. UNIX(R) processes do not execute synchronously so logical operations are rarely atomic. [[secure-bufferov]] == Buffer Overflows Buffer Overflows have been around since the very beginnings of the von Neumann crossref:bibliography[COD,1] architecture. They first gained widespread notoriety in 1988 with the Morris Internet worm. Unfortunately, the same basic attack remains effective today. By far the most common type of buffer overflow attack is based on corrupting the stack. Most modern computer systems use a stack to pass arguments to procedures and to store local variables. A stack is a last in first out (LIFO) buffer in the high memory area of a process image. When a program invokes a function a new "stack frame" is created. This stack frame consists of the arguments passed to the function as well as a dynamic amount of local variable space. The "stack pointer" is a register that holds the current location of the top of the stack. Since this value is constantly changing as new values are pushed onto the top of the stack, many implementations also provide a "frame pointer" that is located near the beginning of a stack frame so that local variables can more easily be addressed relative to this value. crossref:bibliography[COD,1] The return address for function calls is also stored on the stack, and this is the cause of stack-overflow exploits since overflowing a local variable in a function can overwrite the return address of that function, potentially allowing a malicious user to execute any code he or she wants. Although stack-based attacks are by far the most common, it would also be possible to overrun the stack with a heap-based (malloc/free) attack. The C programming language does not perform automatic bounds checking on arrays or pointers as many other languages do. In addition, the standard C library is filled with a handful of very dangerous functions. [.informaltable] [cols="1,1", frame="none"] |=== |`strcpy`(char *dest, const char *src) | May overflow the dest buffer |`strcat`(char *dest, const char *src) | May overflow the dest buffer |`getwd`(char *buf) | May overflow the buf buffer |`gets`(char *s) | May overflow the s buffer |`[vf]scanf`(const char *format, ...) | May overflow its arguments. |`realpath`(char *path, char resolved_path[]) | May overflow the path buffer |`[v]sprintf`(char *str, const char *format, ...) | May overflow the str buffer. |=== === Example Buffer Overflow The following example code contains a buffer overflow designed to overwrite the return address and skip the instruction immediately following the function call. (Inspired by crossref:bibliography[Phrack,4]) [.programlisting] .... #include void manipulate(char *buffer) { char newbuffer[80]; strcpy(newbuffer,buffer); } int main() { char ch,buffer[4096]; int i=0; while ((buffer[i++] = getchar()) != '\n') {}; i=1; manipulate(buffer); i=2; printf("The value of i is : %d\n",i); return 0; } .... Let us examine what the memory image of this process would look like if we were to input 160 spaces into our little program before hitting return. -[XXX figure here!] +[ XXX figure here! ] Obviously more malicious input can be devised to execute actual compiled instructions (such as exec(/bin/sh)). === Avoiding Buffer Overflows The most straightforward solution to the problem of stack-overflows is to always use length restricted memory and string copy functions. `strncpy` and `strncat` are part of the standard C library. These functions accept a length value as a parameter which should be no larger than the size of the destination buffer. These functions will then copy up to 'length' bytes from the source to the destination. However there are a number of problems with these functions. Neither function guarantees NUL termination if the size of the input buffer is as large as the destination. The length parameter is also used inconsistently between strncpy and strncat so it is easy for programmers to get confused as to their proper usage. There is also a significant performance loss compared to `strcpy` when copying a short string into a large buffer since `strncpy` NUL fills up the size specified. Another memory copy implementation exists to get around these problems. The `strlcpy` and `strlcat` functions guarantee that they will always null terminate the destination string when given a non-zero length argument. ==== Compiler based run-time bounds checking Unfortunately there is still a very large assortment of code in public use which blindly copies memory around without using any of the bounded copy routines we just discussed. Fortunately, there is a way to help prevent such attacks - run-time bounds checking, which is implemented by several C/C++ compilers. ProPolice is one such compiler feature, and is integrated into man:gcc[1] versions 4.1 and later. It replaces and extends the earlier StackGuard man:gcc[1] extension. ProPolice helps to protect against stack-based buffer overflows and other attacks by laying pseudo-random numbers in key areas of the stack before calling any function. When a function returns, these "canaries" are checked and if they are found to have been changed the executable is immediately aborted. Thus any attempt to modify the return address or other variable stored on the stack in an attempt to get malicious code to run is unlikely to succeed, as the attacker would have to also manage to leave the pseudo-random canaries untouched. Recompiling your application with ProPolice is an effective means of stopping most buffer-overflow attacks, but it can still be compromised. ==== Library based run-time bounds checking Compiler-based mechanisms are completely useless for binary-only software for which you cannot recompile. For these situations there are a number of libraries which re-implement the unsafe functions of the C-library (`strcpy`, `fscanf`, `getwd`, etc..) and ensure that these functions can never write past the stack pointer. * libsafe * libverify * libparanoia Unfortunately these library-based defenses have a number of shortcomings. These libraries only protect against a very small set of security related issues and they neglect to fix the actual problem. These defenses may fail if the application was compiled with -fomit-frame-pointer. Also, the LD_PRELOAD and LD_LIBRARY_PATH environment variables can be overwritten/unset by the user. [[secure-setuid]] == SetUID issues There are at least 6 different IDs associated with any given process, and you must therefore be very careful with the access that your process has at any given time. In particular, all seteuid applications should give up their privileges as soon as it is no longer required. The real user ID can only be changed by a superuser process. The login program sets this when a user initially logs in and it is seldom changed. The effective user ID is set by the `exec()` functions if a program has its seteuid bit set. An application can call `seteuid()` at any time to set the effective user ID to either the real user ID or the saved set-user-ID. When the effective user ID is set by `exec()` functions, the previous value is saved in the saved set-user-ID. [[secure-chroot]] == Limiting your program's environment The traditional method of restricting a process is with the `chroot()` system call. This system call changes the root directory from which all other paths are referenced for a process and any child processes. For this call to succeed the process must have execute (search) permission on the directory being referenced. The new environment does not actually take effect until you `chdir()` into your new environment. It should also be noted that a process can easily break out of a chroot environment if it has root privilege. This could be accomplished by creating device nodes to read kernel memory, attaching a debugger to a process outside of the man:chroot[8] environment, or in many other creative ways. The behavior of the `chroot()` system call can be controlled somewhat with the kern.chroot_allow_open_directories `sysctl` variable. When this value is set to 0, `chroot()` will fail with EPERM if there are any directories open. If set to the default value of 1, then `chroot()` will fail with EPERM if there are any directories open and the process is already subject to a `chroot()` call. For any other value, the check for open directories will be bypassed completely. === FreeBSD's jail functionality The concept of a Jail extends upon the `chroot()` by limiting the powers of the superuser to create a true `virtual server'. Once a prison is set up all network communication must take place through the specified IP address, and the power of "root privilege" in this jail is severely constrained. While in a prison, any tests of superuser power within the kernel using the `suser()` call will fail. However, some calls to `suser()` have been changed to a new interface `suser_xxx()`. This function is responsible for recognizing or denying access to superuser power for imprisoned processes. A superuser process within a jailed environment has the power to: * Manipulate credential with `setuid`, `seteuid`, `setgid`, `setegid`, `setgroups`, `setreuid`, `setregid`, `setlogin` * Set resource limits with `setrlimit` * Modify some sysctl nodes (kern.hostname) * `chroot()` * Set flags on a vnode: `chflags`, `fchflags` * Set attributes of a vnode such as file permission, owner, group, size, access time, and modification time. * Bind to privileged ports in the Internet domain (ports < 1024) `Jail` is a very useful tool for running applications in a secure environment but it does have some shortcomings. Currently, the IPC mechanisms have not been converted to the `suser_xxx` so applications such as MySQL cannot be run within a jail. Superuser access may have a very limited meaning within a jail, but there is no way to specify exactly what "very limited" means. === POSIX(R).1e Process Capabilities POSIX(R) has released a working draft that adds event auditing, access control lists, fine grained privileges, information labeling, and mandatory access control. This is a work in progress and is the focus of the http://www.trustedbsd.org/[TrustedBSD] project. Some of the initial work has been committed to FreeBSD-CURRENT (cap_set_proc(3)). [[secure-trust]] == Trust An application should never assume that anything about the users environment is sane. This includes (but is certainly not limited to): user input, signals, environment variables, resources, IPC, mmaps, the filesystem working directory, file descriptors, the # of open files, etc. You should never assume that you can catch all forms of invalid input that a user might supply. Instead, your application should use positive filtering to only allow a specific subset of inputs that you deem safe. Improper data validation has been the cause of many exploits, especially with CGI scripts on the world wide web. For filenames you need to be extra careful about paths ("../", "/"), symbolic links, and shell escape characters. Perl has a really cool feature called "Taint" mode which can be used to prevent scripts from using data derived outside the program in an unsafe way. This mode will check command line arguments, environment variables, locale information, the results of certain syscalls (`readdir()`, `readlink()`, `getpwxxx()`), and all file input. [[secure-race-conditions]] == Race Conditions A race condition is anomalous behavior caused by the unexpected dependence on the relative timing of events. In other words, a programmer incorrectly assumed that a particular event would always happen before another. Some of the common causes of race conditions are signals, access checks, and file opens. Signals are asynchronous events by nature so special care must be taken in dealing with them. Checking access with `access(2)` then `open(2)` is clearly non-atomic. Users can move files in between the two calls. Instead, privileged applications should `seteuid()` and then call `open()` directly. Along the same lines, an application should always set a proper umask before `open()` to obviate the need for spurious `chmod()` calls. diff --git a/documentation/content/en/books/handbook/advanced-networking/_index.adoc b/documentation/content/en/books/handbook/advanced-networking/_index.adoc index 182c424c57..b2862f063d 100644 --- a/documentation/content/en/books/handbook/advanced-networking/_index.adoc +++ b/documentation/content/en/books/handbook/advanced-networking/_index.adoc @@ -1,2394 +1,2394 @@ --- title: Chapter 35. Advanced Networking part: IV. Network Communication prev: books/handbook/firewalls next: books/handbook/partv description: "Advanced networking in FreeBSD: basics of gateways and routes, CARP, how to configure multiple VLANs on FreeBSD, etc" tags: ["Advanced Networking", "Handbook", "gateway", "routes", "wireless", "tethering", "bluetooth", "bridging", "CARP", "VLAN", "WiFi"] showBookMenu: true weight: 39 params: path: "/books/handbook/advanced-networking/" --- [[advanced-networking]] = Advanced Networking :doctype: book :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumoffset: 35 :partnums: :source-highlighter: rouge :experimental: :images-path: books/handbook/advanced-networking/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] [[advanced-networking-synopsis]] == Synopsis This chapter covers a number of advanced networking topics. Read this chapter to learn: * The basics of gateways and routes. * How to set up USB tethering. * How to set up IEEE(R) 802.11 and Bluetooth(R) devices. * How to make FreeBSD act as a bridge. * How to set up network PXE booting. * How to enable and utilize the features of the Common Address Redundancy Protocol (CARP) in FreeBSD. * How to configure multiple VLANs on FreeBSD. * How to configure a bluetooth headset. Before reading this chapter: * Understand the basics of the [.filename]#/etc/rc# scripts. * Be familiar with basic network terminology. * Understand basic network configuration on FreeBSD (crossref:network[network,FreeBSD network]). * Know how to configure and install a new FreeBSD kernel (crossref:kernelconfig[kernelconfig,Configuring the FreeBSD Kernel]). * Know how to install additional third-party software (crossref:ports[ports,Installing Applications: Packages and Ports]). [[network-routing]] == Gateways and Routes _Routing_ is the mechanism that allows a system to find the network path to another system. A _route_ is a defined pair of addresses which represent the "destination" and a "gateway". The route indicates that when trying to get to the specified destination, send the packets through the specified gateway. There are three types of destinations: individual hosts, subnets, and "default". The "default route" is used if no other routes apply. There are also three types of gateways: individual hosts, interfaces, also called links, and Ethernet hardware (MAC) addresses. Known routes are stored in a routing table. This section provides an overview of routing basics. It then demonstrates how to configure a FreeBSD system as a router and offers some troubleshooting tips. [[network-routing-default]] === Routing Basics To view the routing table of a FreeBSD system, use man:netstat[1]: [source,shell] .... % netstat -r Routing tables Internet: Destination Gateway Flags Refs Use Netif Expire default outside-gw UGS 37 418 em0 localhost localhost UH 0 181 lo0 test0 0:e0:b5:36:cf:4f UHLW 5 63288 re0 77 10.20.30.255 link#1 UHLW 1 2421 example.com link#1 UC 0 0 host1 0:e0:a8:37:8:1e UHLW 3 4601 lo0 host2 0:e0:a8:37:8:1e UHLW 0 5 lo0 => host2.example.com link#1 UC 0 0 224 link#1 UC 0 0 .... The entries in this example are as follows: default:: The first route in this table specifies the `default` route. When the local system needs to make a connection to a remote host, it checks the routing table to determine if a known path exists. If the remote host matches an entry in the table, the system checks to see if it can connect using the interface specified in that entry. + If the destination does not match an entry, or if all known paths fail, the system uses the entry for the default route. For hosts on a local area network, the `Gateway` field in the default route is set to the system which has a direct connection to the Internet. When reading this entry, verify that the `Flags` column indicates that the gateway is usable (`UG`). + The default route for a machine which itself is functioning as the gateway to the outside world will be the gateway machine at the Internet Service Provider (ISP). localhost:: The second route is the `localhost` route. The interface specified in the `Netif` column for `localhost` is [.filename]#lo0#, also known as the loopback device. This indicates that all traffic for this destination should be internal, rather than sending it out over the network. MAC address:: The addresses beginning with `0:e0:` are MAC addresses. FreeBSD will automatically identify any hosts, `test0` in the example, on the local Ethernet and add a route for that host over the Ethernet interface, [.filename]#re0#. This type of route has a timeout, seen in the `Expire` column, which is used if the host does not respond in a specific amount of time. When this happens, the route to this host will be automatically deleted. These hosts are identified using the Routing Information Protocol (RIP), which calculates routes to local hosts based upon a shortest path determination. subnet:: FreeBSD will automatically add subnet routes for the local subnet. -In this example, `10.20.30.255` is the broadcast address for the subnet `10.20.30` and `example.com` is the domain name associated with that subnet. +In this example, `10.20.30.255` is the broadcast address for the subnet `10.20.30` and `example.com` is the domain name associated with that subnet. The designation `link#1` refers to the first Ethernet card in the machine. + Local network hosts and local subnets have their routes automatically configured by a daemon called man:routed[8]. If it is not running, only routes which are statically defined by the administrator will exist. host:: The `host1` line refers to the host by its Ethernet address. Since it is the sending host, FreeBSD knows to use the loopback interface ([.filename]#lo0#) rather than the Ethernet interface. + The two `host2` lines represent aliases which were created using man:ifconfig[8]. The `=>` symbol after the [.filename]#lo0# interface says that an alias has been set in addition to the loopback address. Such routes only show up on the host that supports the alias and all other hosts on the local network will have a `link#1` line for such routes. 224:: The final line (destination subnet `224`) deals with multicasting. Various attributes of each route can be seen in the `Flags` column. crossref:advanced-networking[routeflags,Commonly Seen Routing Table Flags] summarizes some of these flags and their meanings: [[routeflags]] .Commonly Seen Routing Table Flags [cols="1,1", frame="none", options="header"] |=== | Flag | Purpose |U |The route is active (up). |H |The route destination is a single host. |G |Send anything for this destination on to this gateway, which will figure out from there where to send it. |S |This route was statically configured. |C |Clones a new route based upon this route for machines to connect to. This type of route is normally used for local networks. |W |The route was auto-configured based upon a local area network (clone) route. |L |Route involves references to Ethernet (link) hardware. |=== On a FreeBSD system, the default route can be defined in [.filename]#/etc/rc.conf# by specifying the IP address of the default gateway: [.programlisting] .... defaultrouter="10.20.30.1" .... It is also possible to manually add the route using `route`: [source,shell] .... # route add default 10.20.30.1 .... Note that manually added routes will not survive a reboot. For more information on manual manipulation of network routing tables, refer to man:route[8]. [[network-static-routes]] === Configuring a Router with Static Routes A FreeBSD system can be configured as the default gateway, or router, for a network if it is a dual-homed system. A dual-homed system is a host which resides on at least two different networks. Typically, each network is connected to a separate network interface, though IP aliasing can be used to bind multiple addresses, each on a different subnet, to one physical interface. In order for the system to forward packets between interfaces, FreeBSD must be configured as a router. Internet standards and good engineering practice prevent the FreeBSD Project from enabling this feature by default, but it can be configured to start at boot by adding this line to [.filename]#/etc/rc.conf#: [.programlisting] .... gateway_enable="YES" # Set to YES if this host will be a gateway .... To enable routing now, set the man:sysctl[8] variable `net.inet.ip.forwarding` to `1`. To stop routing, reset this variable to `0`. The routing table of a router needs additional routes so it knows how to reach other networks. Routes can be either added manually using static routes or routes can be automatically learned using a routing protocol. Static routes are appropriate for small networks and this section describes how to add a static routing entry for a small network. [NOTE] ==== For large networks, static routes quickly become unscalable. FreeBSD comes with the standard BSD routing daemon man:routed[8], which provides the routing protocols RIP, versions 1 and 2, and IRDP. Support for the BGP and OSPF routing protocols can be installed using the package:net/quagga[] package or port. ==== Consider the following network: image::static-routes.png[] In this scenario, `RouterA` is a FreeBSD machine that is acting as a router to the rest of the Internet. It has a default route set to `10.0.0.1` which allows it to connect with the outside world. `RouterB` is already configured to use `192.168.1.1` as its default gateway. Before adding any static routes, the routing table on `RouterA` looks like this: [source,shell] .... % netstat -nr Routing tables Internet: Destination Gateway Flags Refs Use Netif Expire default 10.0.0.1 UGS 0 49378 xl0 127.0.0.1 127.0.0.1 UH 0 6 lo0 10.0.0.0/24 link#1 UC 0 0 xl0 192.168.1.0/24 link#2 UC 0 0 xl1 .... With the current routing table, `RouterA` does not have a route to the `192.168.2.0/24` network. The following command adds the `Internal Net 2` network to ``RouterA``'s routing table using `192.168.1.2` as the next hop: [source,shell] .... # route add -net 192.168.2.0/24 192.168.1.2 .... Now, `RouterA` can reach any host on the `192.168.2.0/24` network. However, the routing information will not persist if the FreeBSD system reboots. If a static route needs to be persistent, add it to [.filename]#/etc/rc.conf#: [.programlisting] .... # Add Internal Net 2 as a persistent static route static_routes="internalnet2" route_internalnet2="-net 192.168.2.0/24 192.168.1.2" .... The `static_routes` configuration variable is a list of strings separated by a space, where each string references a route name. The variable `route_internalnet2` contains the static route for that route name. Using more than one string in `static_routes` creates multiple static routes. The following shows an example of adding static routes for the `192.168.0.0/24` and `192.168.1.0/24` networks: [.programlisting] .... static_routes="net1 net2" route_net1="-net 192.168.0.0/24 192.168.0.1" route_net2="-net 192.168.1.0/24 192.168.1.1" .... [[network-routing-troubleshooting]] === Troubleshooting When an address space is assigned to a network, the service provider configures their routing tables so that all traffic for the network will be sent to the link for the site. But how do external sites know to send their packets to the network's ISP? There is a system that keeps track of all assigned address spaces and defines their point of connection to the Internet backbone, or the main trunk lines that carry Internet traffic across the country and around the world. Each backbone machine has a copy of a master set of tables, which direct traffic for a particular network to a specific backbone carrier, and from there down the chain of service providers until it reaches a particular network. It is the task of the service provider to advertise to the backbone sites that they are the point of connection, and thus the path inward, for a site. This is known as route propagation. Sometimes, there is a problem with route propagation and some sites are unable to connect. Perhaps the most useful command for trying to figure out where routing is breaking down is `traceroute`. It is useful when `ping` fails. When using `traceroute`, include the address of the remote host to connect to. The output will show the gateway hosts along the path of the attempt, eventually either reaching the target host, or terminating because of a lack of connection. For more information, refer to man:traceroute[8]. [[network-routing-multicast]] === Multicast Considerations FreeBSD natively supports both multicast applications and multicast routing. Multicast applications do not require any special configuration in order to run on FreeBSD. Support for multicast routing requires that the following option be compiled into a custom kernel: [.programlisting] .... options MROUTING .... The multicast routing daemon, mrouted can be installed using the package:net/mrouted[] package or port. This daemon implements the DVMRP multicast routing protocol and is configured by editing [.filename]#/usr/local/etc/mrouted.conf# in order to set up the tunnels and DVMRP. The installation of mrouted also installs map-mbone and mrinfo, as well as their associated man pages. Refer to these for configuration examples. [NOTE] ==== DVMRP has largely been replaced by the PIM protocol in many multicast installations. Refer to man:pim[4] for more information. ==== [[configtuning-virtual-hosts]] == Virtual Hosts A common use of FreeBSD is virtual site hosting, where one server appears to the network as many servers. This is achieved by assigning multiple network addresses to a single interface. A given network interface has one "real" address, and may have any number of "alias" addresses. These aliases are normally added by placing alias entries in [.filename]#/etc/rc.conf#, as seen in this example: [source,shell] .... # sysrc ifconfig_fxp0_alias0="inet xxx.xxx.xxx.xxx netmask xxx.xxx.xxx.xxx" .... Alias entries must start with `alias__0__` using a sequential number such as `alias0`, `alias1`, and so on. The configuration process will stop at the first missing number. The calculation of alias netmasks is important. For a given interface, there must be one address which correctly represents the network's netmask. Any other addresses which fall within this network must have a netmask of all ``1``s, expressed as either `255.255.255.255` or `0xffffffff`. For example, consider the case where the `fxp0` interface is connected to two networks: `10.1.1.0` with a netmask of `255.255.255.0` and `202.0.75.16` with a netmask of `255.255.255.240`. The system is to be configured to appear in the ranges `10.1.1.1` through `10.1.1.5` and `202.0.75.17` through `202.0.75.20`. Only the first address in a given network range should have a real netmask. All the rest (`10.1.1.2` through `10.1.1.5` and `202.0.75.18` through `202.0.75.20`) must be configured with a netmask of `255.255.255.255`. The following [.filename]#/etc/rc.conf# entries configure the adapter correctly for this scenario: [source,shell] .... # sysrc ifconfig_fxp0="inet 10.1.1.1 netmask 255.255.255.0" # sysrc ifconfig_fxp0_alias0="inet 10.1.1.2 netmask 255.255.255.255" # sysrc ifconfig_fxp0_alias1="inet 10.1.1.3 netmask 255.255.255.255" # sysrc ifconfig_fxp0_alias2="inet 10.1.1.4 netmask 255.255.255.255" # sysrc ifconfig_fxp0_alias3="inet 10.1.1.5 netmask 255.255.255.255" # sysrc ifconfig_fxp0_alias4="inet 202.0.75.17 netmask 255.255.255.240" # sysrc ifconfig_fxp0_alias5="inet 202.0.75.18 netmask 255.255.255.255" # sysrc ifconfig_fxp0_alias6="inet 202.0.75.19 netmask 255.255.255.255" # sysrc ifconfig_fxp0_alias7="inet 202.0.75.20 netmask 255.255.255.255" .... A simpler way to express this is with a space-separated list of IP address ranges. The first address will be given the indicated subnet mask and the additional addresses will have a subnet mask of `255.255.255.255`. [source,shell] .... # sysrc ifconfig_fxp0_aliases="inet 10.1.1.1-5/24 inet 202.0.75.17-20/28" .... [[network-advanced-wireless]] == Wireless Advanced Authentication FreeBSD supports different ways of connecting to a wireless network. This section describes how to perform advanced authentication to a Wireless Network. To make a connection and basic authentication to a wireless network the section crossref:network[wireless-authentication,Connection and Authentication to a Wireless Network] in the Network Chapter describes how to do it. [[network-wireless-wpa-eap-tls]] === WPA with EAP-TLS The second way to use WPA is with an 802.1X backend authentication server. In this case, WPA is called WPA Enterprise to differentiate it from the less secure WPA Personal. Authentication in WPA Enterprise is based on the Extensible Authentication Protocol (EAP). EAP does not come with an encryption method. Instead, EAP is embedded inside an encrypted tunnel. There are many EAP authentication methods, but EAP-TLS, EAP-TTLS, and EAP-PEAP are the most common. EAP with Transport Layer Security (EAP-TLS) is a well-supported wireless authentication protocol since it was the first EAP method to be certified by the http://www.wi-fi.org/[Wi-Fi Alliance]. EAP-TLS requires three certificates to run: the certificate of the Certificate Authority (CA) installed on all machines, the server certificate for the authentication server, and one client certificate for each wireless client. In this EAP method, both the authentication server and wireless client authenticate each other by presenting their respective certificates, and then verify that these certificates were signed by the organization's CA. As previously, the configuration is done via [.filename]#/etc/wpa_supplicant.conf#: [.programlisting] .... network={ ssid="freebsdap" <.> proto=RSN <.> key_mgmt=WPA-EAP <.> eap=TLS <.> identity="loader" <.> ca_cert="/etc/certs/cacert.pem" <.> client_cert="/etc/certs/clientcert.pem" <.> private_key="/etc/certs/clientkey.pem" <.> private_key_passwd="freebsdmallclient" <.> } .... <.> This field indicates the network name (SSID). <.> This example uses the RSN IEEE(R) 802.11i protocol, also known as WPA2. <.> The `key_mgmt` line refers to the key management protocol to use. In this example, it is WPA using EAP authentication. <.> This field indicates the EAP method for the connection. <.> The `identity` field contains the identity string for EAP. <.> The `ca_cert` field indicates the pathname of the CA certificate file. This file is needed to verify the server certificate. <.> The `client_cert` line gives the pathname to the client certificate file. This certificate is unique to each wireless client of the network. <.> The `private_key` field is the pathname to the client certificate private key file. <.> The `private_key_passwd` field contains the passphrase for the private key. Then, add the following lines to [.filename]#/etc/rc.conf#: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="WPA DHCP" .... The next step is to bring up the interface: [source,shell] .... # service netif start Starting wpa_supplicant. DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 7 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 15 DHCPACK from 192.168.0.20 bound to 192.168.0.254 -- renewal in 300 seconds. wlan0: flags=8843 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.254 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet DS/11Mbps mode 11g status: associated ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 3:128-bit txpower 21.5 bmiss 7 scanvalid 450 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst roaming MANUAL .... It is also possible to bring up the interface manually using man:wpa_supplicant[8] and man:ifconfig[8]. [[network-wireless-wpa-eap-ttls]] === WPA with EAP-TTLS With EAP-TLS, both the authentication server and the client need a certificate. With EAP-TTLS, a client certificate is optional. This method is similar to a web server which creates a secure SSL tunnel even if visitors do not have client-side certificates. EAP-TTLS uses an encrypted TLS tunnel for safe transport of the authentication data. The required configuration can be added to [.filename]#/etc/wpa_supplicant.conf#: [.programlisting] .... network={ ssid="freebsdap" proto=RSN key_mgmt=WPA-EAP eap=TTLS <.> identity="test" <.> password="test" <.> ca_cert="/etc/certs/cacert.pem" <.> phase2="auth=MD5" <.> } .... <.> This field specifies the EAP method for the connection. <.> The `identity` field contains the identity string for EAP authentication inside the encrypted TLS tunnel. <.> The `password` field contains the passphrase for the EAP authentication. <.> The `ca_cert` field indicates the pathname of the CA certificate file. This file is needed to verify the server certificate. <.> This field specifies the authentication method used in the encrypted TLS tunnel. In this example, EAP with MD5-Challenge is used. The "inner authentication" phase is often called "phase2". Next, add the following lines to [.filename]#/etc/rc.conf#: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="WPA DHCP" .... The next step is to bring up the interface: [source,shell] .... # service netif start Starting wpa_supplicant. DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 7 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 15 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 21 DHCPACK from 192.168.0.20 bound to 192.168.0.254 -- renewal in 300 seconds. wlan0: flags=8843 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.254 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet DS/11Mbps mode 11g status: associated ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 3:128-bit txpower 21.5 bmiss 7 scanvalid 450 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst roaming MANUAL .... [[network-wireless-wpa-eap-peap]] === WPA with EAP-PEAP [NOTE] ==== PEAPv0/EAP-MSCHAPv2 is the most common PEAP method. In this chapter, the term PEAP is used to refer to that method. ==== Protected EAP (PEAP) is designed as an alternative to EAP-TTLS and is the most used EAP standard after EAP-TLS. In a network with mixed operating systems, PEAP should be the most supported standard after EAP-TLS. PEAP is similar to EAP-TTLS as it uses a server-side certificate to authenticate clients by creating an encrypted TLS tunnel between the client and the authentication server, which protects the ensuing exchange of authentication information. PEAP authentication differs from EAP-TTLS as it broadcasts the username in the clear and only the password is sent in the encrypted TLS tunnel. EAP-TTLS will use the TLS tunnel for both the username and password. Add the following lines to [.filename]#/etc/wpa_supplicant.conf# to configure the EAP-PEAP related settings: [.programlisting] .... network={ ssid="freebsdap" proto=RSN key_mgmt=WPA-EAP eap=PEAP <.> identity="test" <.> password="test" <.> ca_cert="/etc/certs/cacert.pem" <.> phase1="peaplabel=0" <.> phase2="auth=MSCHAPV2" <.> } .... <.> This field specifies the EAP method for the connection. <.> The `identity` field contains the identity string for EAP authentication inside the encrypted TLS tunnel. <.> The `password` field contains the passphrase for the EAP authentication. <.> The `ca_cert` field indicates the pathname of the CA certificate file. This file is needed to verify the server certificate. <.> This field contains the parameters for the first phase of authentication, the TLS tunnel. According to the authentication server used, specify a specific label for authentication. Most of the time, the label will be "client EAP encryption" which is set by using `peaplabel=0`. More information can be found in man:wpa_supplicant.conf[5]. <.> This field specifies the authentication protocol used in the encrypted TLS tunnel. In the case of PEAP, it is `auth=MSCHAPV2`. Add the following to [.filename]#/etc/rc.conf#: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="WPA DHCP" .... Then, bring up the interface: [source,shell] .... # service netif start Starting wpa_supplicant. DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 7 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 15 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 21 DHCPACK from 192.168.0.20 bound to 192.168.0.254 -- renewal in 300 seconds. wlan0: flags=8843 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.254 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet DS/11Mbps mode 11g status: associated ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 3:128-bit txpower 21.5 bmiss 7 scanvalid 450 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst roaming MANUAL .... [[wireless-ad-hoc-mode]] == Wireless Ad-hoc Mode IBSS mode, also called ad-hoc mode, is designed for point to point connections. For example, to establish an ad-hoc network between the machines `A` and `B`, choose two IP addresses and a SSID. On `A`: [source,shell] .... # ifconfig wlan0 create wlandev ath0 wlanmode adhoc # ifconfig wlan0 inet 192.168.0.1 netmask 255.255.255.0 ssid freebsdap # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether 00:11:95:c3:0d:ac inet 192.168.0.1 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet autoselect mode 11g status: running ssid freebsdap channel 2 (2417 Mhz 11g) bssid 02:11:95:c3:0d:ac country US ecm authmode OPEN privacy OFF txpower 21.5 scanvalid 60 protmode CTS wme burst .... The `adhoc` parameter indicates that the interface is running in IBSS mode. `B` should now be able to detect `A`: [source,shell] .... # ifconfig wlan0 create wlandev ath0 wlanmode adhoc # ifconfig wlan0 up scan SSID/MESH ID BSSID CHAN RATE S:N INT CAPS freebsdap 02:11:95:c3:0d:ac 2 54M -64:-96 100 IS WME .... The `I` in the output confirms that `A` is in ad-hoc mode. Now, configure `B` with a different IP address: [source,shell] .... # ifconfig wlan0 inet 192.168.0.2 netmask 255.255.255.0 ssid freebsdap # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.2 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet autoselect mode 11g status: running ssid freebsdap channel 2 (2417 Mhz 11g) bssid 02:11:95:c3:0d:ac country US ecm authmode OPEN privacy OFF txpower 21.5 scanvalid 60 protmode CTS wme burst .... Both `A` and `B` are now ready to exchange information. [[network-wireless-ap]] === FreeBSD Host Access Points FreeBSD can act as an Access Point (AP) which eliminates the need to buy a hardware AP or run an ad-hoc network. This can be particularly useful when a FreeBSD machine is acting as a gateway to another network such as the Internet. [[network-wireless-ap-basic]] ==== Basic Settings Before configuring a FreeBSD machine as an AP, the kernel must be configured with the appropriate networking support for the wireless card as well as the security protocols being used. For more details, see crossref:advanced-networking[network-wireless-ap-basic, Basic Settings]. [NOTE] ==== The NDIS driver wrapper for Windows(R) drivers does not currently support AP operation. Only native FreeBSD wireless drivers support AP mode. ==== Once wireless networking support is loaded, check if the wireless device supports the host-based access point mode, also known as hostap mode: [source,shell] .... # ifconfig wlan0 create wlandev ath0 # ifconfig wlan0 list caps drivercaps=6f85edc1 cryptocaps=1f .... This output displays the card's capabilities. The `HOSTAP` word confirms that this wireless card can act as an AP. Various supported ciphers are also listed: WEP, TKIP, and AES. This information indicates which security protocols can be used on the AP. The wireless device can only be put into hostap mode during the creation of the network pseudo-device, so a previously created device must be destroyed first: [source,shell] .... # ifconfig wlan0 destroy .... then regenerated with the correct option before setting the other parameters: [source,shell] .... # ifconfig wlan0 create wlandev ath0 wlanmode hostap # ifconfig wlan0 inet 192.168.0.1 netmask 255.255.255.0 ssid freebsdap mode 11g channel 1 .... Use man:ifconfig[8] again to see the status of the [.filename]#wlan0# interface: [source,shell] .... # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether 00:11:95:c3:0d:ac inet 192.168.0.1 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet autoselect mode 11g status: running ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode OPEN privacy OFF txpower 21.5 scanvalid 60 protmode CTS wme burst dtimperiod 1 -dfs .... The `hostap` parameter indicates the interface is running in the host-based access point mode. The interface configuration can be done automatically at boot time by adding the following lines to [.filename]#/etc/rc.conf#: [.programlisting] .... wlans_ath0="wlan0" create_args_wlan0="wlanmode hostap" ifconfig_wlan0="inet 192.168.0.1 netmask 255.255.255.0 ssid freebsdap mode 11g channel 1" .... ==== Host-based Access Point Without Authentication or Encryption Although it is not recommended to run an AP without any authentication or encryption, this is a simple way to check if the AP is working. This configuration is also important for debugging client issues. Once the AP is configured, initiate a scan from another wireless machine to find the AP: [source,shell] .... # ifconfig wlan0 create wlandev ath0 # ifconfig wlan0 up scan SSID/MESH ID BSSID CHAN RATE S:N INT CAPS freebsdap 00:11:95:c3:0d:ac 1 54M -66:-96 100 ES WME .... The client machine found the AP and can be associated with it: [source,shell] .... # ifconfig wlan0 inet 192.168.0.2 netmask 255.255.255.0 ssid freebsdap # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.2 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet OFDM/54Mbps mode 11g status: associated ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode OPEN privacy OFF txpower 21.5 bmiss 7 scanvalid 60 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst .... [[network-wireless-ap-wpa]] ==== WPA2 Host-based Access Point This section focuses on setting up a FreeBSD access point using the WPA2 security protocol. More details regarding WPA and the configuration of WPA-based wireless clients can be found in crossref:advanced-networking[network-wireless-wpa-eap-tls, WPA with EAP-TLS]. The man:hostapd[8] daemon is used to deal with client authentication and key management on the WPA2-enabled AP. The following configuration operations are performed on the FreeBSD machine acting as the AP. Once the AP is correctly working, man:hostapd[8] can be automatically started at boot with this line in [.filename]#/etc/rc.conf#: [.programlisting] .... hostapd_enable="YES" .... Before trying to configure man:hostapd[8], first configure the basic settings introduced in crossref:advanced-networking[network-wireless-ap-basic, Basic Settings]. ===== WPA2-PSK WPA2-PSK is intended for small networks where the use of a backend authentication server is not possible or desired. The configuration is done in [.filename]#/etc/hostapd.conf#: [.programlisting] .... interface=wlan0 <.> debug=1 <.> ctrl_interface=/var/run/hostapd <.> ctrl_interface_group=wheel <.> ssid=freebsdap <.> wpa=2 <.> wpa_passphrase=freebsdmall <.> wpa_key_mgmt=WPA-PSK <.> wpa_pairwise=CCMP <.> .... <.> Wireless interface used for the access point. <.> Level of verbosity used during the execution of man:hostapd[8]. A value of `1` represents the minimal level. <.> Pathname of the directory used by man:hostapd[8] to store domain socket files for communication with external programs such as man:hostapd_cli[8]. The default value is used in this example. <.> The group allowed to access the control interface files. <.> The wireless network name, or SSID, that will appear in wireless scans. <.> Enable WPA and specify which WPA authentication protocol will be required. A value of `2` configures the AP for WPA2 and is recommended. Set to `1` only if the obsolete WPA is required. <.> ASCII passphrase for WPA authentication. <.> The key management protocol to use. This example sets WPA-PSK. <.> Encryption algorithms accepted by the access point. In this example, only the CCMP (AES) cipher is accepted. CCMP is an alternative to TKIP and is strongly preferred when possible. TKIP should be allowed only when there are stations incapable of using CCMP. The next step is to start man:hostapd[8]: [source,shell] .... # service hostapd forcestart .... [source,shell] .... # ifconfig wlan0 wlan0: flags=8943 metric 0 mtu 1500 ether 04:f0:21:16:8e:10 inet6 fe80::6f0:21ff:fe16:8e10%wlan0 prefixlen 64 scopeid 0x9 nd6 options=21 media: IEEE 802.11 Wireless Ethernet autoselect mode 11na status: running ssid No5ignal channel 36 (5180 MHz 11a ht/40+) bssid 04:f0:21:16:8e:10 country US ecm authmode WPA2/802.11i privacy MIXED deftxkey 2 AES-CCM 2:128-bit AES-CCM 3:128-bit txpower 17 mcastrate 6 mgmtrate 6 scanvalid 60 ampdulimit 64k ampdudensity 8 shortgi wme burst dtimperiod 1 -dfs groups: wlan .... Once the AP is running, the clients can associate with it. See crossref:advanced-networking[network-wireless-ap-basic, Basic Settings] for more details. It is possible to see the stations associated with the AP using `ifconfig _wlan0_ list sta`. [[network-usb-tethering]] == USB Tethering Many cellphones provide the option to share their data connection over USB (often called "tethering"). This feature uses one of RNDIS, CDC, or a custom Apple(R) iPhone(R)/iPad(R) protocol. * Android(TM) devices generally use the man:urndis[4] driver. * Apple(R) devices use the man:ipheth[4] driver. * Older devices will often use the man:cdce[4] driver. Before attaching a device, load the appropriate driver into the kernel: [source,shell] .... # kldload if_urndis # kldload if_cdce # kldload if_ipheth .... Once the device is attached ``ue``_0_ will be available for use like a normal network device. Be sure that the "USB tethering" option is enabled on the device. To make this change permanent and load the driver as a module at boot time, place the appropriate line of the following in [.filename]#/boot/loader.conf#: [source,shell] .... if_urndis_load="YES" if_cdce_load="YES" if_ipheth_load="YES" .... [[network-bluetooth]] == Bluetooth Bluetooth is a wireless technology for creating personal networks operating in the 2.4 GHz unlicensed band, with a range of 10 meters. Networks are usually formed ad-hoc from portable devices such as cellular phones, handhelds, and laptops. Unlike Wi-Fi wireless technology, Bluetooth offers higher level service profiles, such as FTP-like file servers, file pushing, voice transport, serial line emulation, and more. This section describes the use of a USB Bluetooth dongle on a FreeBSD system. It then describes the various Bluetooth protocols and utilities. === Loading Bluetooth Support The Bluetooth stack in FreeBSD is implemented using the man:netgraph[4] framework. A broad variety of Bluetooth USB dongles is supported by man:ng_ubt[4]. Broadcom BCM2033 based Bluetooth devices are supported by the man:ubtbcmfw[4] and man:ng_ubt[4] drivers. The 3Com Bluetooth PC Card 3CRWB60-A is supported by the man:ng_bt3c[4] driver. Serial and UART based Bluetooth devices are supported by man:ng_h4[4] and man:hcseriald[8]. Before attaching a device, determine which of the above drivers it uses, then load the driver. For example, if the device uses the man:ng_ubt[4] driver: [source,shell] .... # kldload ng_ubt .... If the Bluetooth device will be attached to the system during system startup, the system can be configured to load the module at boot time by adding the driver to [.filename]#/boot/loader.conf#: [.programlisting] .... ng_ubt_load="YES" .... Once the driver is loaded, plug in the USB dongle. If the driver load was successful, output similar to the following should appear on the console and in [.filename]#/var/log/messages#: [source,shell] .... ubt0: vendor 0x0a12 product 0x0001, rev 1.10/5.25, addr 2 ubt0: Interface 0 endpoints: interrupt=0x81, bulk-in=0x82, bulk-out=0x2 ubt0: Interface 1 (alt.config 5) endpoints: isoc-in=0x83, isoc-out=0x3, wMaxPacketSize=49, nframes=6, buffer size=294 .... To start and stop the Bluetooth stack, use its startup script. It is a good idea to stop the stack before unplugging the device. Starting the bluetooth stack might require man:hcsecd[8] to be started. When starting the stack, the output should be similar to the following: [source,shell] .... # service bluetooth start ubt0 BD_ADDR: 00:02:72:00:d4:1a Features: 0xff 0xff 0xf 00 00 00 00 00 <3-Slot> <5-Slot> Max. ACL packet size: 192 bytes Number of ACL packets: 8 Max. SCO packet size: 64 bytes Number of SCO packets: 8 .... === Finding Other Bluetooth Devices The Host Controller Interface (HCI) provides a uniform method for accessing Bluetooth baseband capabilities. In FreeBSD, a netgraph HCI node is created for each Bluetooth device. For more details, refer to man:ng_hci[4]. One of the most common tasks is discovery of Bluetooth devices within RF proximity. This operation is called _inquiry_. Inquiry and other HCI related operations are done using man:hccontrol[8]. The example below shows how to find out which Bluetooth devices are in range. The list of devices should be displayed in a few seconds. Note that a remote device will only answer the inquiry if it is set to _discoverable_ mode. [source,shell] .... % hccontrol -n ubt0hci inquiry Inquiry result, num_responses=1 Inquiry result #0 BD_ADDR: 00:80:37:29:19:a4 Page Scan Rep. Mode: 0x1 Page Scan Period Mode: 00 Page Scan Mode: 00 Class: 52:02:04 Clock offset: 0x78ef Inquiry complete. Status: No error [00] .... The `BD_ADDR` is the unique address of a Bluetooth device, similar to the MAC address of a network card. This address is needed for further communication with a device and it is possible to assign a human readable name to a `BD_ADDR`. Information regarding the known Bluetooth hosts is contained in [.filename]#/etc/bluetooth/hosts#. The following example shows how to obtain the human readable name that was assigned to the remote device: [source,shell] .... % hccontrol -n ubt0hci remote_name_request 00:80:37:29:19:a4 BD_ADDR: 00:80:37:29:19:a4 Name: Pav's T39 .... If an inquiry is performed on a remote Bluetooth device, it will find the computer as "your.host.name (ubt0)". The name assigned to the local device can be changed at any time. Remote devices can be assigned aliases in [.filename]#/etc/bluetooth/hosts#. More information about [.filename]#/etc/bluetooth/hosts# file might be found in man:bluetooth.hosts[5]. The Bluetooth system provides a point-to-point connection between two Bluetooth units, or a point-to-multipoint connection which is shared among several Bluetooth devices. The following example shows how to create a connection to a remote device: [source,shell] .... % hccontrol -n ubt0hci create_connection BT_ADDR .... `create_connection` accepts `BT_ADDR` as well as host aliases in [.filename]#/etc/bluetooth/hosts#. The following example shows how to obtain the list of active baseband connections for the local device: [source,shell] .... % hccontrol -n ubt0hci read_connection_list Remote BD_ADDR Handle Type Mode Role Encrypt Pending Queue State 00:80:37:29:19:a4 41 ACL 0 MAST NONE 0 0 OPEN .... A _connection handle_ is useful when termination of the baseband connection is required, though it is normally not required to do this by hand. The stack will automatically terminate inactive baseband connections. [source,shell] .... # hccontrol -n ubt0hci disconnect 41 Connection handle: 41 Reason: Connection terminated by local host [0x16] .... Type `hccontrol help` for a complete listing of available HCI commands. Most of the HCI commands do not require superuser privileges. === Device Pairing By default, Bluetooth communication is not authenticated, and any device can talk to any other device. A Bluetooth device, such as a cellular phone, may choose to require authentication to provide a particular service. Bluetooth authentication is normally done with a _PIN code_, an ASCII string up to 16 characters in length. The user is required to enter the same PIN code on both devices. Once the user has entered the PIN code, both devices will generate a _link key_. After that, the link key can be stored either in the devices or in a persistent storage. Next time, both devices will use the previously generated link key. This procedure is called _pairing_. Note that if the link key is lost by either device, the pairing must be repeated. The man:hcsecd[8] daemon is responsible for handling Bluetooth authentication requests. The default configuration file is [.filename]#/etc/bluetooth/hcsecd.conf#. An example section for a cellular phone with the PIN code set to `1234` is shown below: [.programlisting] .... device { bdaddr 00:80:37:29:19:a4; name "Pav's T39"; key nokey; pin "1234"; } .... The only limitation on PIN codes is length. Some devices, such as Bluetooth headsets, may have a fixed PIN code built in. The `-d` switch forces man:hcsecd[8] to stay in the foreground, so it is easy to see what is happening. Set the remote device to receive pairing and initiate the Bluetooth connection to the remote device. The remote device should indicate that pairing was accepted and request the PIN code. Enter the same PIN code listed in [.filename]#hcsecd.conf#. Now the computer and the remote device are paired. Alternatively, pairing can be initiated on the remote device. The following line can be added to [.filename]#/etc/rc.conf# to configure man:hcsecd[8] to start automatically on system start: [.programlisting] .... hcsecd_enable="YES" .... The following is a sample of the man:hcsecd[8] daemon output: [.programlisting] .... hcsecd[16484]: Got Link_Key_Request event from 'ubt0hci', remote bdaddr 0:80:37:29:19:a4 hcsecd[16484]: Found matching entry, remote bdaddr 0:80:37:29:19:a4, name 'Pav's T39', link key doesn't exist hcsecd[16484]: Sending Link_Key_Negative_Reply to 'ubt0hci' for remote bdaddr 0:80:37:29:19:a4 hcsecd[16484]: Got PIN_Code_Request event from 'ubt0hci', remote bdaddr 0:80:37:29:19:a4 hcsecd[16484]: Found matching entry, remote bdaddr 0:80:37:29:19:a4, name 'Pav's T39', PIN code exists hcsecd[16484]: Sending PIN_Code_Reply to 'ubt0hci' for remote bdaddr 0:80:37:29:19:a4 .... === Network Access with PPP Profiles A Dial-Up Networking (DUN) profile can be used to configure a cellular phone as a wireless modem for connecting to a dial-up Internet access server. It can also be used to configure a computer to receive data calls from a cellular phone. Network access with a PPP profile can be used to provide LAN access for a single Bluetooth device or multiple Bluetooth devices. It can also provide PC to PC connection using PPP networking over serial cable emulation. In FreeBSD, these profiles are implemented with man:ppp[8] and the man:rfcomm_pppd[8] wrapper which converts a Bluetooth connection into something PPP can use. Before a profile can be used, a new PPP label must be created in [.filename]#/etc/ppp/ppp.conf#. Consult man:rfcomm_pppd[8] for examples. In this example, man:rfcomm_pppd[8] is used to open a connection to a remote device with a `BD_ADDR` of `00:80:37:29:19:a4` on a DUNRFCOMM channel: [source,shell] .... # rfcomm_pppd -a 00:80:37:29:19:a4 -c -C dun -l rfcomm-dialup .... The actual channel number will be obtained from the remote device using the SDP protocol. It is possible to specify the RFCOMM channel by hand, and in this case man:rfcomm_pppd[8] will not perform the SDP query. Use man:sdpcontrol[8] to find out the RFCOMM channel on the remote device. In order to provide network access with the PPPLAN service, man:sdpd[8] must be running and a new entry for LAN clients must be created in [.filename]#/etc/ppp/ppp.conf#. Consult man:rfcomm_pppd[8] for examples. Finally, start the RFCOMMPPP server on a valid RFCOMM channel number. The RFCOMMPPP server will automatically register the Bluetooth LAN service with the local SDP daemon. The example below shows how to start the RFCOMMPPP server. [source,shell] .... # rfcomm_pppd -s -C 7 -l rfcomm-server .... === Bluetooth Protocols This section provides an overview of the various Bluetooth protocols, their function, and associated utilities. ==== Logical Link Control and Adaptation Protocol (L2CAP) The Logical Link Control and Adaptation Protocol (L2CAP) provides connection-oriented and connectionless data services to upper layer protocols. L2CAP permits higher level protocols and applications to transmit and receive L2CAP data packets up to 64 kilobytes in length. L2CAP is based around the concept of _channels_. A channel is a logical connection on top of a baseband connection, where each channel is bound to a single protocol in a many-to-one fashion. Multiple channels can be bound to the same protocol, but a channel cannot be bound to multiple protocols. Each L2CAP packet received on a channel is directed to the appropriate higher level protocol. Multiple channels can share the same baseband connection. In FreeBSD, a netgraph L2CAP node is created for each Bluetooth device. This node is normally connected to the downstream Bluetooth HCI node and upstream Bluetooth socket nodes. The default name for the L2CAP node is "devicel2cap". For more details refer to man:ng_l2cap[4]. -A useful command is man:l2ping[8], which can be used to ping other devices. +A useful command is man:l2ping[8], which can be used to ping other devices. Some Bluetooth implementations might not return all of the data sent to them, so `0 bytes` in the following example is normal. [source,shell] .... # l2ping -a 00:80:37:29:19:a4 0 bytes from 0:80:37:29:19:a4 seq_no=0 time=48.633 ms result=0 0 bytes from 0:80:37:29:19:a4 seq_no=1 time=37.551 ms result=0 0 bytes from 0:80:37:29:19:a4 seq_no=2 time=28.324 ms result=0 0 bytes from 0:80:37:29:19:a4 seq_no=3 time=46.150 ms result=0 .... The man:l2control[8] utility is used to perform various operations on L2CAP nodes. This example shows how to obtain the list of logical connections (channels) and the list of baseband connections for the local device: [source,shell] .... % l2control -a 00:02:72:00:d4:1a read_channel_list L2CAP channels: Remote BD_ADDR SCID/ DCID PSM IMTU/ OMTU State 00:07:e0:00:0b:ca 66/ 64 3 132/ 672 OPEN % l2control -a 00:02:72:00:d4:1a read_connection_list L2CAP connections: Remote BD_ADDR Handle Flags Pending State 00:07:e0:00:0b:ca 41 O 0 OPEN .... Another diagnostic tool is man:btsockstat[1]. It is similar to man:netstat[1], but for Bluetooth network-related data structures. The example below shows the same logical connection as man:l2control[8] above. [source,shell] .... % btsockstat Active L2CAP sockets PCB Recv-Q Send-Q Local address/PSM Foreign address CID State c2afe900 0 0 00:02:72:00:d4:1a/3 00:07:e0:00:0b:ca 66 OPEN Active RFCOMM sessions L2PCB PCB Flag MTU Out-Q DLCs State c2afe900 c2b53380 1 127 0 Yes OPEN Active RFCOMM sockets PCB Recv-Q Send-Q Local address Foreign address Chan DLCI State c2e8bc80 0 250 00:02:72:00:d4:1a 00:07:e0:00:0b:ca 3 6 OPEN .... ==== Radio Frequency Communication (RFCOMM) The RFCOMM protocol provides emulation of serial ports over the L2CAP protocol. RFCOMM is a simple transport protocol, with additional provisions for emulating the 9 circuits of RS-232 (EIATIA-232-E) serial ports. It supports up to 60 simultaneous connections (RFCOMM channels) between two Bluetooth devices. For the purposes of RFCOMM, a complete communication path involves two applications running on the communication endpoints with a communication segment between them. RFCOMM is intended to cover applications that make use of the serial ports of the devices in which they reside. The communication segment is a direct connect Bluetooth link from one device to another. -RFCOMM is only concerned with the connection between the devices in the direct connect case, or between the device and a modem in the network case. +RFCOMM is only concerned with the connection between the devices in the direct connect case, or between the device and a modem in the network case. RFCOMM can support other configurations, such as modules that communicate via Bluetooth wireless technology on one side and provide a wired interface on the other side. In FreeBSD, RFCOMM is implemented at the Bluetooth sockets layer. ==== Service Discovery Protocol (SDP) The Service Discovery Protocol (SDP) provides the means for client applications to discover the existence of services provided by server applications as well as the attributes of those services. The attributes of a service include the type or class of service offered and the mechanism or protocol information needed to utilize the service. SDP involves communication between a SDP server and a SDP client. The server maintains a list of service records that describe the characteristics of services associated with the server. Each service record contains information about a single service. A client may retrieve information from a service record maintained by the SDP server by issuing a SDP request. If the client, or an application associated with the client, decides to use a service, it must open a separate connection to the service provider in order to utilize the service. SDP provides a mechanism for discovering services and their attributes, but it does not provide a mechanism for utilizing those services. Normally, a SDP client searches for services based on some desired characteristics of the services. However, there are times when it is desirable to discover which types of services are described by an SDP server's service records without any prior information about the services. This process of looking for any offered services is called _browsing_. The Bluetooth SDP server, man:sdpd[8], and command line client, man:sdpcontrol[8], are included in the standard FreeBSD installation. The following example shows how to perform a SDP browse query. [source,shell] .... % sdpcontrol -a 00:01:03:fc:6e:ec browse Record Handle: 00000000 Service Class ID List: Service Discovery Server (0x1000) Protocol Descriptor List: L2CAP (0x0100) Protocol specific parameter #1: u/int/uuid16 1 Protocol specific parameter #2: u/int/uuid16 1 Record Handle: 0x00000001 Service Class ID List: Browse Group Descriptor (0x1001) Record Handle: 0x00000002 Service Class ID List: LAN Access Using PPP (0x1102) Protocol Descriptor List: L2CAP (0x0100) RFCOMM (0x0003) Protocol specific parameter #1: u/int8/bool 1 Bluetooth Profile Descriptor List: LAN Access Using PPP (0x1102) ver. 1.0 .... Note that each service has a list of attributes, such as the RFCOMM channel. Depending on the service, the user might need to make note of some of the attributes. Some Bluetooth implementations do not support service browsing and may return an empty list. In this case, it is possible to search for the specific service. The example below shows how to search for the OBEX Object Push (OPUSH) service: [source,shell] .... % sdpcontrol -a 00:01:03:fc:6e:ec search OPUSH .... Offering services on FreeBSD to Bluetooth clients is done with the man:sdpd[8] server. The following line can be added to [.filename]#/etc/rc.conf#: [.programlisting] .... sdpd_enable="YES" .... Then the man:sdpd[8] daemon can be started with: [source,shell] .... # service sdpd start .... The local server application that wants to provide a Bluetooth service to remote clients will register the service with the local SDP daemon. An example of such an application is man:rfcomm_pppd[8]. Once started, it will register the Bluetooth LAN service with the local SDP daemon. The list of services registered with the local SDP server can be obtained by issuing a SDP browse query via the local control channel: [source,shell] .... # sdpcontrol -l browse .... ==== OBEX Object Push (OPUSH) Object Exchange (OBEX) is a widely used protocol for simple file transfers between mobile devices. Its main use is in infrared communication, where it is used for generic file transfers between notebooks or PDAs, and for sending business cards or calendar entries between cellular phones and other devices with Personal Information Manager (PIM) applications. The OBEX server and client are implemented by obexapp, which can be installed using the package:comms/obexapp[] package or port. The OBEX client is used to push and/or pull objects from the OBEX server. An example object is a business card or an appointment. The OBEX client can obtain the RFCOMM channel number from the remote device via SDP. This can be done by specifying the service name instead of the RFCOMM channel number. Supported service names are: `IrMC`, `FTRN`, and `OPUSH`. It is also possible to specify the RFCOMM channel as a number. Below is an example of an OBEX session where the device information object is pulled from the cellular phone, and a new object, the business card, is pushed into the phone's directory. [source,shell] .... % obexapp -a 00:80:37:29:19:a4 -C IrMC obex> get telecom/devinfo.txt devinfo-t39.txt Success, response: OK, Success (0x20) obex> put new.vcf Success, response: OK, Success (0x20) obex> di Success, response: OK, Success (0x20) .... In order to provide the OPUSH service, man:sdpd[8] must be running and a root folder, where all incoming objects will be stored, must be created. The default path to the root folder is [.filename]#/var/spool/obex#. Finally, start the OBEX server on a valid RFCOMM channel number. The OBEX server will automatically register the OPUSH service with the local SDP daemon. The example below shows how to start the OBEX server. [source,shell] .... # obexapp -s -C 10 .... ==== Serial Port Profile (SPP) The Serial Port Profile (SPP) allows Bluetooth devices to perform serial cable emulation. This profile allows legacy applications to use Bluetooth as a cable replacement, through a virtual serial port abstraction. In FreeBSD, man:rfcomm_sppd[1] implements SPP and a pseudo tty is used as a virtual serial port abstraction. The example below shows how to connect to a remote device's serial port service. A RFCOMM channel does not have to be specified as man:rfcomm_sppd[1] can obtain it from the remote device via SDP. To override this, specify a RFCOMM channel on the command line. [source,shell] .... # rfcomm_sppd -a 00:07:E0:00:0B:CA -t rfcomm_sppd[94692]: Starting on /dev/pts/6... /dev/pts/6 .... Once connected, the pseudo tty can be used as serial port: [source,shell] .... # cu -l /dev/pts/6 .... The pseudo tty is printed on stdout and can be read by wrapper scripts: [.programlisting] .... PTS=`rfcomm_sppd -a 00:07:E0:00:0B:CA -t` cu -l $PTS .... === Troubleshooting By default, when FreeBSD is accepting a new connection, it tries to perform a role switch and become master. Some older Bluetooth devices which do not support role switching will not be able to connect. Since role switching is performed when a new connection is being established, it is not possible to ask the remote device if it supports role switching. However, there is a HCI option to disable role switching on the local side: [source,shell] .... # hccontrol -n ubt0hci write_node_role_switch 0 .... To display Bluetooth packets, use the third-party package hcidump, which can be installed using the package:comms/hcidump[] package or port. This utility is similar to man:tcpdump[1] and can be used to display the contents of Bluetooth packets on the terminal and to dump the Bluetooth packets to a file. [[network-bridging]] == Bridging It is sometimes useful to divide a network, such as an Ethernet segment, into network segments without having to create IP subnets and use a router to connect the segments together. A device that connects two networks together in this fashion is called a "bridge". A bridge works by learning the MAC addresses of the devices on each of its network interfaces. It forwards traffic between networks only when the source and destination MAC addresses are on different networks. In many respects, a bridge is like an Ethernet switch with very few ports. A FreeBSD system with multiple network interfaces can be configured to act as a bridge. Bridging can be useful in the following situations: Connecting Networks:: The basic operation of a bridge is to join two or more network segments. There are many reasons to use a host-based bridge instead of networking equipment, such as cabling constraints or firewalling. A bridge can also connect a wireless interface running in hostap mode to a wired network and act as an access point. Filtering/Traffic Shaping Firewall:: A bridge can be used when firewall functionality is needed without routing or Network Address Translation (NAT). + An example is a small company that is connected via DSL or ISDN to an ISP. There are thirteen public IP addresses from the ISP and ten computers on the network. In this situation, using a router-based firewall is difficult because of subnetting issues. A bridge-based firewall can be configured without any IP addressing issues. Network Tap:: A bridge can join two network segments in order to inspect all Ethernet frames that pass between them using man:bpf[4] and man:tcpdump[1] on the bridge interface, or by sending a copy of all frames out on an additional interface known as a span port. Layer 2 VPN:: Two Ethernet networks can be joined across an IP link by bridging the networks to an EtherIP tunnel or a man:tap[4] based solution such as OpenVPN. Layer 2 Redundancy:: A network can be connected together with multiple links and use the Spanning Tree Protocol (STP) to block redundant paths. This section describes how to configure a FreeBSD system as a bridge using man:if_bridge[4]. A netgraph bridging driver is also available, and is described in man:ng_bridge[4]. [NOTE] ==== Packet filtering can be used with any firewall package that hooks into the man:pfil[9] framework. The bridge can be used as a traffic shaper with man:altq[4] or man:dummynet[4]. ==== === Enabling the Bridge In FreeBSD, man:if_bridge[4] is a kernel module which is automatically loaded by man:ifconfig[8] when creating a bridge interface. It is also possible to compile bridge support into a custom kernel by adding `device if_bridge` to the custom kernel configuration file. The bridge is created using interface cloning. To create the bridge interface: [source,shell] .... # ifconfig bridge create bridge0 # ifconfig bridge0 bridge0: flags=8802 metric 0 mtu 1500 ether 96:3d:4b:f1:79:7a id 00:00:00:00:00:00 priority 32768 hellotime 2 fwddelay 15 maxage 20 holdcnt 6 proto rstp maxaddr 100 timeout 1200 root id 00:00:00:00:00:00 priority 0 ifcost 0 port 0 .... When a bridge interface is created, it is automatically assigned a randomly generated Ethernet address. The `maxaddr` and `timeout` parameters control how many MAC addresses the bridge will keep in its forwarding table and how many seconds before each entry is removed after it is last seen. The other parameters control how STP operates. Next, specify which network interfaces to add as members of the bridge. For the bridge to forward packets, all member interfaces and the bridge need to be up: [source,shell] .... # ifconfig bridge0 addm fxp0 addm fxp1 up # ifconfig fxp0 up # ifconfig fxp1 up .... The bridge can now forward Ethernet frames between [.filename]#fxp0# and [.filename]#fxp1#. Add the following lines to [.filename]#/etc/rc.conf# so the bridge is created at startup: [.programlisting] .... cloned_interfaces="bridge0" ifconfig_bridge0="addm fxp0 addm fxp1 up" ifconfig_fxp0="up" ifconfig_fxp1="up" .... If the bridge host needs an IP address, set it on the bridge interface, not on the member interfaces. The address can be set statically or via DHCP. This example sets a static IP address: [source,shell] .... # ifconfig bridge0 inet 192.168.0.1/24 .... It is also possible to assign an IPv6 address to a bridge interface. To make the changes permanent, add the addressing information to [.filename]#/etc/rc.conf#. This example uses a dynamic IPv6 address: [.programlisting] .... ifconfig_bridge0_ipv6="inet6 auto_linklocal accept_rtadv" .... [NOTE] ==== When packet filtering is enabled, bridged packets will pass through the filter inbound on the originating interface on the bridge interface, and outbound on the appropriate interfaces. Either stage can be disabled. When direction of the packet flow is important, it is best to firewall on the member interfaces rather than the bridge itself. The bridge has several configurable settings for passing non-IP and IP packets, and layer2 firewalling with man:ipfw[8]. See man:if_bridge[4] for more information. ==== === Enabling Spanning Tree For an Ethernet network to function properly, only one active path can exist between two devices. The STP protocol detects loops and puts redundant links into a blocked state. Should one of the active links fail, STP calculates a different tree and enables one of the blocked paths to restore connectivity to all points in the network. The Rapid Spanning Tree Protocol (RSTP or 802.1w) provides backwards compatibility with legacy STP. RSTP provides faster convergence and exchanges information with neighboring switches to quickly transition to forwarding mode without creating loops. FreeBSD supports RSTP and STP as operating modes, with RSTP being the default mode. STP can be enabled on member interfaces using man:ifconfig[8]. For a bridge with [.filename]#fxp0# and [.filename]#fxp1# as the current interfaces, enable STP with: [source,shell] .... # ifconfig bridge0 stp fxp0 stp fxp1 bridge0: flags=8843 metric 0 mtu 1500 ether d6:cf:d5:a0:94:6d id 00:01:02:4b:d4:50 priority 32768 hellotime 2 fwddelay 15 maxage 20 holdcnt 6 proto rstp maxaddr 100 timeout 1200 root id 00:01:02:4b:d4:50 priority 32768 ifcost 0 port 0 member: fxp0 flags=1c7 port 3 priority 128 path cost 200000 proto rstp role designated state forwarding member: fxp1 flags=1c7 port 4 priority 128 path cost 200000 proto rstp role designated state forwarding .... This bridge has a spanning tree ID of `00:01:02:4b:d4:50` and a priority of `32768`. As the `root id` is the same, it indicates that this is the root bridge for the tree. Another bridge on the network also has STP enabled: [source,shell] .... bridge0: flags=8843 metric 0 mtu 1500 ether 96:3d:4b:f1:79:7a id 00:13:d4:9a:06:7a priority 32768 hellotime 2 fwddelay 15 maxage 20 holdcnt 6 proto rstp maxaddr 100 timeout 1200 root id 00:01:02:4b:d4:50 priority 32768 ifcost 400000 port 4 member: fxp0 flags=1c7 port 4 priority 128 path cost 200000 proto rstp role root state forwarding member: fxp1 flags=1c7 port 5 priority 128 path cost 200000 proto rstp role designated state forwarding .... The line `root id 00:01:02:4b:d4:50 priority 32768 ifcost 400000 port 4` shows that the root bridge is `00:01:02:4b:d4:50` and has a path cost of `400000` from this bridge. The path to the root bridge is via `port 4` which is [.filename]#fxp0#. === Bridge Interface Parameters Several `ifconfig` parameters are unique to bridge interfaces. This section summarizes some common uses for these parameters. The complete list of available parameters is described in man:ifconfig[8]. private:: A private interface does not forward any traffic to any other port that is also designated as a private interface. The traffic is blocked unconditionally so no Ethernet frames will be forwarded, including ARP packets. If traffic needs to be selectively blocked, a firewall should be used instead. span:: A span port transmits a copy of every Ethernet frame received by the bridge. The number of span ports configured on a bridge is unlimited, but if an interface is designated as a span port, it cannot also be used as a regular bridge port. This is most useful for snooping a bridged network passively on another host connected to one of the span ports of the bridge. For example, to send a copy of all frames out the interface named [.filename]#fxp4#: + [source,shell] .... # ifconfig bridge0 span fxp4 .... sticky:: If a bridge member interface is marked as sticky, dynamically learned address entries are treated as static entries in the forwarding cache. Sticky entries are never aged out of the cache or replaced, even if the address is seen on a different interface. This gives the benefit of static address entries without the need to pre-populate the forwarding table. Clients learned on a particular segment of the bridge cannot roam to another segment. + An example of using sticky addresses is to combine the bridge with VLANs in order to isolate customer networks without wasting IP address space. Consider that `CustomerA` is on `vlan100`, `CustomerB` is on `vlan101`, and the bridge has the address `192.168.0.1`: + [source,shell] .... # ifconfig bridge0 addm vlan100 sticky vlan100 addm vlan101 sticky vlan101 # ifconfig bridge0 inet 192.168.0.1/24 .... + In this example, both clients see `192.168.0.1` as their default gateway. Since the bridge cache is sticky, one host cannot spoof the MAC address of the other customer in order to intercept their traffic. + Any communication between the VLANs can be blocked using a firewall or, as seen in this example, private interfaces: + [source,shell] .... # ifconfig bridge0 private vlan100 private vlan101 .... + The customers are completely isolated from each other and the full `/24` address range can be allocated without subnetting. + The number of unique source MAC addresses behind an interface can be limited. Once the limit is reached, packets with unknown source addresses are dropped until an existing host cache entry expires or is removed. + The following example sets the maximum number of Ethernet devices for `CustomerA` on `vlan100` to 10: + [source,shell] .... # ifconfig bridge0 ifmaxaddr vlan100 10 .... Bridge interfaces also support monitor mode, where the packets are discarded after man:bpf[4] processing and are not processed or forwarded further. This can be used to multiplex the input of two or more interfaces into a single man:bpf[4] stream. This is useful for reconstructing the traffic for network taps that transmit the RX/TX signals out through two separate interfaces. For example, to read the input from four network interfaces as one stream: [source,shell] .... # ifconfig bridge0 addm fxp0 addm fxp1 addm fxp2 addm fxp3 monitor up # tcpdump -i bridge0 .... === SNMP Monitoring The bridge interface and STP parameters can be monitored via man:bsnmpd[1] which is included in the FreeBSD base system. The exported bridge MIBs conform to IETF standards so any SNMP client or monitoring package can be used to retrieve the data. To enable monitoring on the bridge, uncomment this line in [.filename]#/etc/snmpd.config# by removing the beginning `+#+` symbol: [.programlisting] .... begemotSnmpdModulePath."bridge" = "/usr/lib/snmp_bridge.so" .... Other configuration settings, such as community names and access lists, may need to be modified in this file. See man:bsnmpd[1] and man:snmp_bridge[3] for more information. Once these edits are saved, add this line to [.filename]#/etc/rc.conf#: [.programlisting] .... bsnmpd_enable="YES" .... Then, start man:bsnmpd[1]: [source,shell] .... # service bsnmpd start .... The following examples use the Net-SNMP software (package:net-mgmt/net-snmp[]) to query a bridge from a client system. The package:net-mgmt/bsnmptools[] port can also be used. From the SNMP client which is running Net-SNMP, add the following lines to [.filename]#$HOME/.snmp/snmp.conf# in order to import the bridge MIB definitions: [.programlisting] .... mibdirs +/usr/share/snmp/mibs mibs +BRIDGE-MIB:RSTP-MIB:BEGEMOT-MIB:BEGEMOT-BRIDGE-MIB .... To monitor a single bridge using the IETF BRIDGE-MIB (RFC4188): [source,shell] .... % snmpwalk -v 2c -c public bridge1.example.com mib-2.dot1dBridge BRIDGE-MIB::dot1dBaseBridgeAddress.0 = STRING: 66:fb:9b:6e:5c:44 BRIDGE-MIB::dot1dBaseNumPorts.0 = INTEGER: 1 ports BRIDGE-MIB::dot1dStpTimeSinceTopologyChange.0 = Timeticks: (189959) 0:31:39.59 centi-seconds BRIDGE-MIB::dot1dStpTopChanges.0 = Counter32: 2 BRIDGE-MIB::dot1dStpDesignatedRoot.0 = Hex-STRING: 80 00 00 01 02 4B D4 50 ... BRIDGE-MIB::dot1dStpPortState.3 = INTEGER: forwarding(5) BRIDGE-MIB::dot1dStpPortEnable.3 = INTEGER: enabled(1) BRIDGE-MIB::dot1dStpPortPathCost.3 = INTEGER: 200000 BRIDGE-MIB::dot1dStpPortDesignatedRoot.3 = Hex-STRING: 80 00 00 01 02 4B D4 50 BRIDGE-MIB::dot1dStpPortDesignatedCost.3 = INTEGER: 0 BRIDGE-MIB::dot1dStpPortDesignatedBridge.3 = Hex-STRING: 80 00 00 01 02 4B D4 50 BRIDGE-MIB::dot1dStpPortDesignatedPort.3 = Hex-STRING: 03 80 BRIDGE-MIB::dot1dStpPortForwardTransitions.3 = Counter32: 1 RSTP-MIB::dot1dStpVersion.0 = INTEGER: rstp(2) .... The `dot1dStpTopChanges.0` value is two, indicating that the STP bridge topology has changed twice. A topology change means that one or more links in the network have changed or failed and a new tree has been calculated. The `dot1dStpTimeSinceTopologyChange.0` value will show when this happened. To monitor multiple bridge interfaces, the private BEGEMOT-BRIDGE-MIB can be used: [source,shell] .... % snmpwalk -v 2c -c public bridge1.example.com enterprises.fokus.begemot.begemotBridge BEGEMOT-BRIDGE-MIB::begemotBridgeBaseName."bridge0" = STRING: bridge0 BEGEMOT-BRIDGE-MIB::begemotBridgeBaseName."bridge2" = STRING: bridge2 BEGEMOT-BRIDGE-MIB::begemotBridgeBaseAddress."bridge0" = STRING: e:ce:3b:5a:9e:13 BEGEMOT-BRIDGE-MIB::begemotBridgeBaseAddress."bridge2" = STRING: 12:5e:4d:74:d:fc BEGEMOT-BRIDGE-MIB::begemotBridgeBaseNumPorts."bridge0" = INTEGER: 1 BEGEMOT-BRIDGE-MIB::begemotBridgeBaseNumPorts."bridge2" = INTEGER: 1 ... BEGEMOT-BRIDGE-MIB::begemotBridgeStpTimeSinceTopologyChange."bridge0" = Timeticks: (116927) 0:19:29.27 centi-seconds BEGEMOT-BRIDGE-MIB::begemotBridgeStpTimeSinceTopologyChange."bridge2" = Timeticks: (82773) 0:13:47.73 centi-seconds BEGEMOT-BRIDGE-MIB::begemotBridgeStpTopChanges."bridge0" = Counter32: 1 BEGEMOT-BRIDGE-MIB::begemotBridgeStpTopChanges."bridge2" = Counter32: 1 BEGEMOT-BRIDGE-MIB::begemotBridgeStpDesignatedRoot."bridge0" = Hex-STRING: 80 00 00 40 95 30 5E 31 BEGEMOT-BRIDGE-MIB::begemotBridgeStpDesignatedRoot."bridge2" = Hex-STRING: 80 00 00 50 8B B8 C6 A9 .... To change the bridge interface being monitored via the `mib-2.dot1dBridge` subtree: [source,shell] .... % snmpset -v 2c -c private bridge1.example.com BEGEMOT-BRIDGE-MIB::begemotBridgeDefaultBridgeIf.0 s bridge2 .... [[network-aggregation]] == Link Aggregation and Failover FreeBSD provides the man:lagg[4] interface which can be used to aggregate multiple network interfaces into one virtual interface in order to provide failover and link aggregation. Failover allows traffic to continue to flow as long as at least one aggregated network interface has an established link. Link aggregation works best on switches which support LACP, as this protocol distributes traffic bi-directionally while responding to the failure of individual links. The aggregation protocols supported by the lagg interface determine which ports are used for outgoing traffic and whether or not a specific port accepts incoming traffic. The following protocols are supported by man:lagg[4]: failover:: This mode sends and receives traffic only through the master port. If the master port becomes unavailable, the next active port is used. The first interface added to the virtual interface is the master port and all subsequently added interfaces are used as failover devices. If failover to a non-master port occurs, the original port becomes master once it becomes available again. loadbalance:: This provides a static setup and does not negotiate aggregation with the peer or exchange frames to monitor the link. If the switch supports LACP, that should be used instead. lacp:: The IEEE(R) 802.3ad Link Aggregation Control Protocol (LACP) negotiates a set of aggregable links with the peer into one or more Link Aggregated Groups (LAGs). Each LAG is composed of ports of the same speed, set to full-duplex operation, and traffic is balanced across the ports in the LAG with the greatest total speed. Typically, there is only one LAG which contains all the ports. In the event of changes in physical connectivity, LACP will quickly converge to a new configuration. + LACP balances outgoing traffic across the active ports based on hashed protocol header information and accepts incoming traffic from any active port. The hash includes the Ethernet source and destination address and, if available, the VLAN tag, and the IPv4 or IPv6 source and destination address. roundrobin:: This mode distributes outgoing traffic using a round-robin scheduler through all active ports and accepts incoming traffic from any active port. Since this mode violates Ethernet frame ordering, it should be used with caution. broadcast:: This mode sends outgoing traffic to all ports configured on the lagg interface, and receives frames on any port. === Configuration Examples This section demonstrates how to configure a Cisco(R) switch and a FreeBSD system for LACP load balancing. It then shows how to configure two Ethernet interfaces in failover mode as well as how to configure failover mode between an Ethernet and a wireless interface. [[networking-lacp-aggregation-cisco]] .LACP Aggregation with a Cisco(R) Switch [example] ==== This example connects two man:fxp[4] Ethernet interfaces on a FreeBSD machine to the first two Ethernet ports on a Cisco(R) switch as a single load balanced and fault tolerant link. More interfaces can be added to increase throughput and fault tolerance. Replace the names of the Cisco(R) ports, Ethernet devices, channel group number, and IP address shown in the example to match the local configuration. Frame ordering is mandatory on Ethernet links and any traffic between two stations always flows over the same physical link, limiting the maximum speed to that of one interface. The transmit algorithm attempts to use as much information as it can to distinguish different traffic flows and balance the flows across the available interfaces. On the Cisco(R) switch, add the _FastEthernet0/1_ and _FastEthernet0/2_ interfaces to channel group _1_: [source,shell] .... interface FastEthernet0/1 channel-group 1 mode active channel-protocol lacp ! interface FastEthernet0/2 channel-group 1 mode active channel-protocol lacp .... On the FreeBSD system, create the man:lagg[4] interface using the physical interfaces _fxp0_ and _fxp1_ and bring the interfaces up with an IP address of _10.0.0.3/24_: [source,shell] .... # ifconfig fxp0 up # ifconfig fxp1 up # ifconfig lagg0 create # ifconfig lagg0 up laggproto lacp laggport fxp0 laggport fxp1 10.0.0.3/24 .... Next, verify the status of the virtual interface: [source,shell] .... # ifconfig lagg0 lagg0: flags=8843 metric 0 mtu 1500 options=8 ether 00:05:5d:71:8d:b8 inet 10.0.0.3 netmask 0xffffff00 broadcast 10.0.0.255 media: Ethernet autoselect status: active laggproto lacp laggport: fxp1 flags=1c laggport: fxp0 flags=1c .... Ports marked as `ACTIVE` are part of the LAG that has been negotiated with the remote switch. Traffic will be transmitted and received through these active ports. Add `-v` to the above command to view the LAG identifiers. To see the port status on the Cisco(R) switch: [source,shell] .... switch# show lacp neighbor Flags: S - Device is requesting Slow LACPDUs F - Device is requesting Fast LACPDUs A - Device is in Active mode P - Device is in Passive mode Channel group 1 neighbors Partner's information: LACP port Oper Port Port Port Flags Priority Dev ID Age Key Number State Fa0/1 SA 32768 0005.5d71.8db8 29s 0x146 0x3 0x3D Fa0/2 SA 32768 0005.5d71.8db8 29s 0x146 0x4 0x3D .... For more detail, type `show lacp neighbor detail`. To retain this configuration across reboots, add the following entries to [.filename]#/etc/rc.conf# on the FreeBSD system: [.programlisting] .... ifconfig_fxp0="up" ifconfig_fxp1="up" cloned_interfaces="lagg0" ifconfig_lagg0="laggproto lacp laggport fxp0 laggport fxp1 10.0.0.3/24" .... ==== [[networking-lagg-failover]] .Failover Mode [example] ==== Failover mode can be used to switch over to a secondary interface if the link is lost on the master interface. To configure failover, make sure that the underlying physical interfaces are up, then create the man:lagg[4] interface. In this example, _fxp0_ is the master interface, _fxp1_ is the secondary interface, and the virtual interface is assigned an IP address of _10.0.0.15/24_: [source,shell] .... # ifconfig fxp0 up # ifconfig fxp1 up # ifconfig lagg0 create # ifconfig lagg0 up laggproto failover laggport fxp0 laggport fxp1 10.0.0.15/24 .... The virtual interface should look something like this: [source,shell] .... # ifconfig lagg0 lagg0: flags=8843 metric 0 mtu 1500 options=8 ether 00:05:5d:71:8d:b8 inet 10.0.0.15 netmask 0xffffff00 broadcast 10.0.0.255 media: Ethernet autoselect status: active laggproto failover laggport: fxp1 flags=0<> laggport: fxp0 flags=5 .... Traffic will be transmitted and received on _fxp0_. If the link is lost on _fxp0_, _fxp1_ will become the active link. If the link is restored on the master interface, it will once again become the active link. To retain this configuration across reboots, add the following entries to [.filename]#/etc/rc.conf#: [.programlisting] .... ifconfig_fxp0="up" ifconfig_fxp1="up" cloned_interfaces="lagg0" ifconfig_lagg0="laggproto failover laggport fxp0 laggport fxp1 10.0.0.15/24" .... ==== [[networking-lagg-wired-and-wireless]] .Failover Mode Between Ethernet and Wireless Interfaces [example] ==== For laptop users, it is usually desirable to configure the wireless device as a secondary which is only used when the Ethernet connection is not available. With man:lagg[4], it is possible to configure a failover which prefers the Ethernet connection for both performance and security reasons, while maintaining the ability to transfer data over the wireless connection. This is achieved by overriding the Ethernet interface's MAC address with that of the wireless interface. [NOTE] -**** +==== In theory, either the Ethernet or wireless MAC address can be changed to match the other. However, some popular wireless interfaces lack support for overriding the MAC address. We therefore recommend overriding the Ethernet MAC address for this purpose. -**** +==== [NOTE] -**** +==== If the driver for the wireless interface is not loaded in the `GENERIC` or custom kernel, and the computer is running FreeBSD {rel121-current}, load the corresponding [.filename]#.ko# in [.filename]#/boot/loader.conf# by adding `*driver_load="YES"*` to that file and rebooting. Another, better way is to load the driver in [.filename]#/etc/rc.conf# by adding it to `kld_list` (see man:rc.conf[5] for details) in that file and rebooting. This is needed because otherwise the driver is not loaded yet at the time the man:lagg[4] interface is set up. -**** +==== In this example, the Ethernet interface, _re0_, is the master and the wireless interface, _wlan0_, is the failover. The _wlan0_ interface was created from the _ath0_ physical wireless interface, and the Ethernet interface will be configured with the MAC address of the wireless interface. First, bring the wireless interface up (replacing _FR_ with the local 2-letter country code), but do not set an IP address. Replace _wlan0_ to match the system's wireless interface name: [source,shell] .... # ifconfig wlan0 create wlandev ath0 country FR ssid my_router up .... Determine the MAC address of the wireless interface like this: [source,shell] .... # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether b8:ee:65:5b:32:59 groups: wlan ssid Bbox-A3BD2403 channel 6 (2437 MHz 11g ht/20) bssid 00:37:b7:56:4b:60 regdomain ETSI country FR indoor ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 2:128-bit txpower 30 bmiss 7 scanvalid 60 protmode CTS ampdulimit 64k ampdudensity 8 shortgi -stbctx stbcrx -ldpc wme burst roaming MANUAL media: IEEE 802.11 Wireless Ethernet MCS mode 11ng status: associated nd6 options=29 .... The `ether` line will contain the MAC address of the specified interface. Now, change the MAC address of the Ethernet interface to match: [source,shell] .... # ifconfig re0 ether b8:ee:65:5b:32:59 .... Make sure the _re0_ interface is up, then create the man:lagg[4] interface with _re0_ as master with failover to _wlan0_: [source,shell] .... # ifconfig re0 up # ifconfig lagg0 create # ifconfig lagg0 up laggproto failover laggport re0 laggport wlan0 .... The virtual interface should look something like this: [source,shell] .... # ifconfig lagg0 lagg0: flags=8843 metric 0 mtu 1500 options=8 ether b8:ee:65:5b:32:59 laggproto failover lagghash l2,l3,l4 laggport: re0 flags=5 laggport: wlan0 flags=0<> groups: lagg media: Ethernet autoselect status: active .... Then, start the DHCP client to obtain an IP address: [source,shell] .... # dhclient lagg0 .... To retain this configuration across reboots, add the following entries to [.filename]#/etc/rc.conf#: [.programlisting] .... ifconfig_re0="ether b8:ee:65:5b:32:59" wlans_ath0="wlan0" ifconfig_wlan0="WPA" create_args_wlan0="country FR" cloned_interfaces="lagg0" ifconfig_lagg0="up laggproto failover laggport re0 laggport wlan0 DHCP" .... ==== [[network-diskless]] == Diskless Operation with PXE The Intel(R) Preboot eXecution Environment (PXE) allows an operating system to boot over the network. For example, a FreeBSD system can boot over the network and operate without a local disk, using file systems mounted from an NFS server. PXE support is usually available in the BIOS. To use PXE when the machine starts, select the `Boot from network` option in the BIOS setup or type a function key during system initialization. In order to provide the files needed for an operating system to boot over the network, a PXE setup also requires properly configured DHCP, TFTP, and NFS servers, where: * Initial parameters, such as an IP address, executable boot filename and location, server name, and root path are obtained from the DHCP server. * The operating system loader file is booted using TFTP. * The file systems are loaded using NFS. When a computer PXE boots, it receives information over DHCP about where to obtain the initial boot loader file. After the host computer receives this information, it downloads the boot loader via TFTP and then executes the boot loader. In FreeBSD, the boot loader file is [.filename]#/boot/pxeboot#. After [.filename]#/boot/pxeboot# executes, the FreeBSD kernel is loaded and the rest of the FreeBSD bootup sequence proceeds, as described in crossref:boot[boot,The FreeBSD Booting Process]. [NOTE] ==== For UEFI PXE based boot, the actual boot loader file to use is [.filename]#/boot/loader.efi#. See the below section crossref:advanced-networking[_debugging_pxe_problems,Debugging PXE Problems] on how to use [.filename]#/boot/loader.efi#. ==== This section describes how to configure these services on a FreeBSD system so that other systems can PXE boot into FreeBSD. Refer to man:diskless[8] for more information. [CAUTION] ==== As described, the system providing these services is insecure. It should live in a protected area of a network and be untrusted by other hosts. ==== [[network-pxe-nfs]] === Setting Up the PXE Environment The steps shown in this section configure the built-in NFS and TFTP servers. The next section demonstrates how to install and configure the DHCP server. In this example, the directory which will contain the files used by PXE users is [.filename]#/b/tftpboot/FreeBSD/install#. It is important that this directory exists and that the same directory name is set in both [.filename]#/etc/inetd.conf# and [.filename]#/usr/local/etc/dhcpd.conf#. [NOTE] ==== The command examples below assume use of the man:sh[1] shell. man:csh[1] and man:tcsh[1] users will need to start a man:sh[1] shell or adapt the commands to man:csh[1] syntax. ==== [.procedure] . Create the root directory which will contain a FreeBSD installation to be NFS mounted: + [source,shell] .... # export NFSROOTDIR=/b/tftpboot/FreeBSD/install # mkdir -p ${NFSROOTDIR} .... . Enable the NFS server by adding this line to [.filename]#/etc/rc.conf#: + [.programlisting] .... nfs_server_enable="YES" .... . Export the diskless root directory via NFS by adding the following to [.filename]#/etc/exports#: + [.programlisting] .... /b -ro -alldirs -maproot=root .... . Start the NFS server: + [source,shell] .... # service nfsd start .... . Enable man:inetd[8] by adding the following line to [.filename]#/etc/rc.conf#: + [.programlisting] .... inetd_enable="YES" .... . Uncomment the following line in [.filename]#/etc/inetd.conf# by making sure it does not start with a `+#+` symbol: + [.programlisting] .... tftp dgram udp wait root /usr/libexec/tftpd tftpd blocksize 1468 -l -s /b/tftpboot .... + [NOTE] ==== The specified tftp blocksize, e.g. 1468 bytes, replaces the default size 512 bytes. Some PXE versions require the TCP version of TFTP. In this case, uncomment the second `tftp` line which contains `stream tcp`. ==== . Start man:inetd[8]: + [source,shell] .... # service inetd start .... . Install the base system into [.filename]#${NFSROOTDIR}#, either by decompressing the official archives or by rebuilding the FreeBSD kernel and userland (refer to crossref:cutting-edge[makeworld,“Updating FreeBSD from Source”] for more detailed instructions, but do not forget to add `DESTDIR=_${NFSROOTDIR}_` when running the `make installkernel` and `make installworld` commands. . Test that the TFTP server works and can download the boot loader which will be obtained via PXE: + [source,shell] .... # tftp localhost tftp> get FreeBSD/install/boot/pxeboot Received 264951 bytes in 0.1 seconds .... . Edit [.filename]#${NFSROOTDIR}/etc/fstab# and create an entry to mount the root file system over NFS: + [.programlisting] .... # Device Mountpoint FSType Options Dump Pass myhost.example.com:/b/tftpboot/FreeBSD/install / nfs ro 0 0 .... + Replace _myhost.example.com_ with the hostname or IP address of the NFS server. In this example, the root file system is mounted read-only in order to prevent NFS clients from potentially deleting the contents of the root file system. . Set the root password in the PXE environment for client machines which are PXE booting : + [source,shell] .... # chroot ${NFSROOTDIR} # passwd .... . If needed, enable man:ssh[1] root logins for client machines which are PXE booting by editing [.filename]#${NFSROOTDIR}/etc/ssh/sshd_config# and enabling `PermitRootLogin`. This option is documented in man:sshd_config[5]. . Perform any other needed customizations of the PXE environment in [.filename]#${NFSROOTDIR}#. These customizations could include things like installing packages or editing the password file with man:vipw[8]. When booting from an NFS root volume, [.filename]#/etc/rc# detects the NFS boot and runs [.filename]#/etc/rc.initdiskless#. In this case, [.filename]#/etc# and [.filename]#/var# need to be memory backed file systems so that these directories are writable but the NFS root directory is read-only: [source,shell] .... # chroot ${NFSROOTDIR} # mkdir -p conf/base # tar -c -v -f conf/base/etc.cpio.gz --format cpio --gzip etc # tar -c -v -f conf/base/var.cpio.gz --format cpio --gzip var .... When the system boots, memory file systems for [.filename]#/etc# and [.filename]#/var# will be created and mounted and the contents of the [.filename]#cpio.gz# files will be copied into them. By default, these file systems have a maximum capacity of 5 megabytes. If the archives do not fit, which is usually the case for [.filename]#/var# when binary packages have been installed, request a larger size by putting the number of 512 byte sectors needed (e.g., 5 megabytes is 10240 sectors) in [.filename]#${NFSROOTDIR}/conf/base/etc/md_size# and [.filename]#${NFSROOTDIR}/conf/base/var/md_size# files for [.filename]#/etc# and [.filename]#/var# file systems respectively. [[network-pxe-setting-up-dhcp]] === Configuring the DHCP Server The DHCP server does not need to be the same machine as the TFTP and NFS server, but it needs to be accessible in the network. DHCP is not part of the FreeBSD base system but can be installed using the package:net/isc-dhcp44-server[] port or package. Once installed, edit the configuration file, [.filename]#/usr/local/etc/dhcpd.conf#. Configure the `next-server`, `filename`, and `root-path` settings as seen in this example: [.programlisting] .... subnet 192.168.0.0 netmask 255.255.255.0 { range 192.168.0.2 192.168.0.3 ; option subnet-mask 255.255.255.0 ; option routers 192.168.0.1 ; option broadcast-address 192.168.0.255 ; option domain-name-servers 192.168.35.35, 192.168.35.36 ; option domain-name "example.com"; # IP address of TFTP server next-server 192.168.0.1 ; # path of boot loader obtained via tftp filename "FreeBSD/install/boot/pxeboot" ; # pxeboot boot loader will try to NFS mount this directory for root FS option root-path "192.168.0.1:/b/tftpboot/FreeBSD/install/" ; } .... The `next-server` directive is used to specify the IP address of the TFTP server. The `filename` directive defines the path to [.filename]#/boot/pxeboot#. A relative filename is used, meaning that [.filename]#/b/tftpboot# is not included in the path. The `root-path` option defines the path to the NFS root file system. Once the edits are saved, enable DHCP at boot time by adding the following line to [.filename]#/etc/rc.conf#: [.programlisting] .... dhcpd_enable="YES" .... Then start the DHCP service: [source,shell] .... # service isc-dhcpd start .... === Debugging PXE Problems Once all of the services are configured and started, PXE clients should be able to automatically load FreeBSD over the network. If a particular client is unable to connect, when that client machine boots up, enter the BIOS configuration menu and confirm that it is set to boot from the network. This section describes some troubleshooting tips for isolating the source of the configuration problem should no clients be able to PXE boot. [.procedure] **** . Use the package:net/wireshark[] package or port to debug the network traffic involved during the PXE booting process, which is illustrated in the diagram below. + .PXE Booting Process with NFS Root Mount image::pxe-nfs.png[] + 1. Client broadcasts a DHCPDISCOVER message. + 2. The DHCP server responds with the IP address, next-server, filename, and root-path values. + 3. The client sends a TFTP request to next-server, asking to retrieve filename. + 4. The TFTP server responds and sends filename to client. + 5. The client executes filename, which is pxeboot(8), which then loads the kernel. When the kernel executes, the root file system specified by root-path is mounted over NFS. + . On the TFTP server, read [.filename]#/var/log/xferlog# to ensure that [.filename]#pxeboot# is being retrieved from the correct location. To test this example configuration: + [source,shell] .... # tftp 192.168.0.1 tftp> get FreeBSD/install/boot/pxeboot Received 264951 bytes in 0.1 seconds .... + The `BUGS` sections in man:tftpd[8] and man:tftp[1] document some limitations with TFTP. . Make sure that the root file system can be mounted via NFS. To test this example configuration: + [source,shell] .... # mount -t nfs 192.168.0.1:/b/tftpboot/FreeBSD/install /mnt .... + . For UEFI PXE based booting, replace the [.filename]#boot/pxeboot# file with the [.filename]#boot/loader.efi# file: [source,shell] .... # chroot ${NFSROOTDIR} # mv boot/pxeboot boot/pxeboot.original # cp boot/loader.efi boot/pxeboot .... **** [[carp]] == Common Address Redundancy Protocol (CARP) The Common Address Redundancy Protocol (CARP) allows multiple hosts to share the same IP address and Virtual Host ID (VHID) in order to provide _high availability_ for one or more services. This means that one or more hosts can fail, and the other hosts will transparently take over so that users do not see a service failure. In addition to the shared IP address, each host has its own IP address for management and configuration. All of the machines that share an IP address have the same VHID. The VHID for each virtual IP address must be unique across the broadcast domain of the network interface. High availability using CARP is built into FreeBSD, though the steps to configure it vary slightly depending upon the FreeBSD version. This section provides the same example configuration for versions before and equal to or after FreeBSD 10. This example configures failover support with three hosts, all with unique IP addresses, but providing the same web content. It has two different masters named `hosta.example.org` and `hostb.example.org`, with a shared backup named `hostc.example.org`. These machines are load balanced with a Round Robin DNS configuration. The master and backup machines are configured identically except for their hostnames and management IP addresses. These servers must have the same configuration and run the same services. When the failover occurs, requests to the service on the shared IP address can only be answered correctly if the backup server has access to the same content. The backup machine has two additional CARP interfaces, one for each of the master content server's IP addresses. When a failure occurs, the backup server will pick up the failed master machine's IP address. [[carp-10x]] === Using CARP Enable boot-time support for CARP by adding an entry for the [.filename]#carp.ko# kernel module in [.filename]#/boot/loader.conf#: [.programlisting] .... carp_load="YES" .... To load the module now without rebooting: [source,shell] .... # kldload carp .... For users who prefer to use a custom kernel, include the following line in the custom kernel configuration file and compile the kernel as described in crossref:kernelconfig[kernelconfig,Configuring the FreeBSD Kernel]: [.programlisting] .... device carp .... The hostname, management IP address and subnet mask, shared IP address, and VHID are all set by adding entries to [.filename]#/etc/rc.conf#. This example is for `hosta.example.org`: [.programlisting] .... hostname="hosta.example.org" ifconfig_em0="inet 192.168.1.3 netmask 255.255.255.0" ifconfig_em0_alias0="inet vhid 1 pass testpass alias 192.168.1.50/32" .... The next set of entries are for `hostb.example.org`. Since it represents a second master, it uses a different shared IP address and VHID. However, the passwords specified with `pass` must be identical as CARP will only listen to and accept advertisements from machines with the correct password. [.programlisting] .... hostname="hostb.example.org" ifconfig_em0="inet 192.168.1.4 netmask 255.255.255.0" ifconfig_em0_alias0="inet vhid 2 pass testpass alias 192.168.1.51/32" .... The third machine, `hostc.example.org`, is configured to handle failover from either master. This machine is configured with two CARPVHIDs, one to handle the virtual IP address for each of the master hosts. The CARP advertising skew, `advskew`, is set to ensure that the backup host advertises later than the master, since `advskew` controls the order of precedence when there are multiple backup servers. [.programlisting] .... hostname="hostc.example.org" ifconfig_em0="inet 192.168.1.5 netmask 255.255.255.0" ifconfig_em0_alias0="inet vhid 1 advskew 100 pass testpass alias 192.168.1.50/32" ifconfig_em0_alias1="inet vhid 2 advskew 100 pass testpass alias 192.168.1.51/32" .... Having two CARPVHIDs configured means that `hostc.example.org` will notice if either of the master servers becomes unavailable. If a master fails to advertise before the backup server, the backup server will pick up the shared IP address until the master becomes available again. [NOTE] ==== If the original master server becomes available again, `hostc.example.org` will not release the virtual IP address back to it automatically. For this to happen, preemption has to be enabled. The feature is disabled by default, it is controlled via the man:sysctl[8] variable `net.inet.carp.preempt`. The administrator can force the backup server to return the IP address to the master: [source,shell] .... # ifconfig em0 vhid 1 state backup .... ==== Once the configuration is complete, either restart networking or reboot each system. High availability is now enabled. CARP functionality can be controlled via several man:sysctl[8] variables documented in the man:carp[4] manual pages. Other actions can be triggered from CARP events by using man:devd[8]. [[network-vlan]] == VLANs VLANs are a way of virtually dividing up a network into many different subnetworks, also referred to as segmenting. Each segment will have its own broadcast domain and be isolated from other VLANs. On FreeBSD, VLANs must be supported by the network card driver. To see which drivers support vlans, refer to the man:vlan[4] manual page. When configuring a VLAN, a couple pieces of information must be known. First, which network interface? Second, what is the VLAN tag? To configure VLANs at run time, with a NIC of `em0` and a VLAN tag of `5` the command would look like this: [source,shell] .... # ifconfig em0.5 create vlan 5 vlandev em0 inet 192.168.20.20/24 .... [NOTE] ==== See how the interface name includes the NIC driver name and the VLAN tag, separated by a period? This is a best practice to make maintaining the VLAN configuration easy when many VLANs are present on a machine. ==== [NOTE] ==== When defining VLANs, ensure that the parent network interface is also configured and enabled. The minimum configuration for the above example would be: [source,shell] .... # ifconfig em0 up .... ==== To configure VLANs at boot time, [.filename]#/etc/rc.conf# must be updated. To duplicate the configuration above, the following will need to be added: [.programlisting] .... vlans_em0="5" ifconfig_em0_5="inet 192.168.20.20/24" .... Additional VLANs may be added, by simply adding the tag to the `vlans_em0` field and adding an additional line configuring the network on that VLAN tag's interface. [NOTE] ==== When defining VLANs in [.filename]#/etc/rc.conf#, make sure that the parent network interface is configured and enabled as well. The minimum configuration for the above example would be: [.programlisting] .... ifconfig_em0="up" .... ==== It is useful to assign a symbolic name to an interface so that when the associated hardware is changed, only a few configuration variables need to be updated. For example, security cameras need to be run over VLAN 1 on `em0`. Later, if the `em0` card is replaced with a card that uses the man:ixgb[4] driver, all references to `em0.1` will not have to change to `ixgb0.1`. To configure VLAN `5`, on the NIC `em0`, assign the interface name `cameras`, and assign the interface an IP address of `_192.168.20.20_` with a `24`-bit prefix, use this command: [source,shell] .... # ifconfig em0.5 create vlan 5 vlandev em0 name cameras inet 192.168.20.20/24 .... For an interface named `video`, use the following: [source,shell] .... # ifconfig video.5 create vlan 5 vlandev video name cameras inet 192.168.20.20/24 .... To apply the changes at boot time, add the following lines to [.filename]#/etc/rc.conf#: [.programlisting] .... vlans_video="cameras" create_args_cameras="vlan 5" ifconfig_cameras="inet 192.168.20.20/24" .... diff --git a/documentation/content/en/books/handbook/wayland/_index.adoc b/documentation/content/en/books/handbook/wayland/_index.adoc index e05df2004d..d9e78df6b1 100644 --- a/documentation/content/en/books/handbook/wayland/_index.adoc +++ b/documentation/content/en/books/handbook/wayland/_index.adoc @@ -1,726 +1,726 @@ --- title: Chapter 6. Wayland part: Part I. Getting Started prev: books/handbook/x11 next: books/handbook/network description: This chapter describes how to install and configure Wayland and compositors on FreeBSD, which provides a graphical user environment tags: ["Wayland", "XWayland", "KDE", "Plasma", "Xfce", "Gnome", "Intel", "AMD", "NVIDIA", "Wayfire", "Sway", "Hikari"] showBookMenu: true weight: 8 params: path: "/books/handbook/wayland/" --- [[wayland]] = Wayland on FreeBSD :doctype: book :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumoffset: 6 :partnums: :source-highlighter: rouge :experimental: :images-path: books/handbook/wayland/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] [[wayland-synopsis]] == Synopsis An installation of FreeBSD using bsdinstall does not automatically install a graphical user interface. This chapter describes how to select, install, and configure a Wayland compositor, which provides a graphical environment. Before reading this chapter: * Know how to install crossref:ports[ports,additional third-party software]. * Know how to identify and configure crossref:x11[x-graphic-card-drivers,drivers for the graphics hardware]. Read this chapter to learn: * How to configure FreeBSD to host a Wayland graphical environment. * How to install and configure a Wayland compositor. * How to run programs designed for the older X Window System. * How to configure remote desktop access to a Wayland graphical environment. [[wayland-overview]] == Wayland Overview Wayland is a communication protocol that can replace a display server such as Xorg. It differs from Xorg in several important ways. First, Wayland is only a protocol that acts as an intermediary between clients using a mechanism which removes the dependency on an X server. Xorg includes both the X11 protocol, used to run remote displays, and the X server, used to accept connections and display windows. Under Wayland, the compositor or window manager provides the display server instead of a traditional X server. Since Wayland is not an X server, traditional X screen connections will need to utilize other methods such as VNC or RDP for remote desktop management. Second, Wayland can manage composite communications between clients and a compositor as a separate entity which does not need to support the X protocols. Wayland is relatively new, and not all software has been updated to run natively without `Xwayland` support. Because Wayland does not provide the X server, and expects compositors to provide that support, X11 window managers that do not yet support Wayland will require that `Xwayland` is not started with the `-rootless` parameter. The `-rootless` parameter, when removed, will restore X11 window manager support. [NOTE] ==== The current NVIDIA(R) driver should work with most wlroots compositors, but it may be a little unstable and not support all features at this time. Volunteers to help work on the NVIDIA(R) DRM are requested. ==== Currently, a lot of software will function with minimal issues on Wayland, including Firefox. And a few desktops are also available, such as the Compiz Fusion replacement, known as Wayfire, and the i3 window manager replacement, Sway. [NOTE] ==== As of May, 2021, plasma5-kwin does support Wayland on FreeBSD. To use Plasma under Wayland, use the `startplasma-wayland` parameter to `ck-launch-session` and tie in dbus with: `dbus-launch --exit-with-x11 ck-launch-session startplasma-wayland` to get it working. ==== For compositors, a kernel supporting the man:evdev[4] driver must exist to utilize the keybinding functionality. This is built into the [.filename]#GENERIC# kernel by default; however, if it has been customized and man:evdev[4] support was stripped out, the man:evdev[4] module will need to be loaded. In addition, users of `Wayland` will need to be members of the `video` group. To quickly make this change, use the `pw` command: [source,shell] ---- pw groupmod video -m user ---- Installing Wayland is simple; there is not a great deal of configuration for the protocol itself. Most of the composition will depend on the chosen compositor. By installing `seatd` now, a step is skipped as part of the compositor installation and configuration as `seatd` is needed to provide non-root access to certain devices. All of the compositors described here should work with package:graphics/drm-kmod[] open source drivers; however, the NVIDIA(R) graphics cards may have issues when using the proprietary drivers. Begin by installing the following packages: [source,shell] ---- # pkg install wayland seatd ---- Once the protocol and supporting packages have been installed, a compositor must create the user interface. Several compositors will be covered in the following sections. All compositors using Wayland will need a runtime directory defined in the environment. Since FreeBSD 14.1, this is created and defined automatically. For previous versions, this is achieved with the following command in the bourne shell: [source,shell] ---- % export XDG_RUNTIME_DIR=/var/run/user/`id -u` ---- It is important to note that most compositors will search the XDG_RUNTIME_DIR directory for the configuration files. In the examples included here, a parameter will be used to specify a configuration file in [.filename]#~/.config# to keep temporary files and configuration files separate. It is recommended that an alias be configured for each compositor to load the designated configuration file. [WARNING] ==== It has been reported that ZFS users may experience issues with some Wayland clients because they need access to `posix_fallocate()` in the runtime directory. While the author could not reproduce this issue on their ZFS system, a recommended workaround is not to use ZFS for the runtime directory and instead use `tmpfs` for the [.filename]#/var/run# directory. In this case, the `tmpfs` file system is used for [.filename]#/var/run# and mounted through the command `mount -t tmpfs tmpfs /var/run` command and then make this change persist across reboots through [.filename]#/etc/fstab#. The XDG_RUNTIME_DIR environment variable could be configured to use [.filename]#/var/run/user/$UID# and avoid potential pitfalls with ZFS. Consider that scenario when reviewing the configuration examples in the following sections. ==== The seatd daemon helps manage access to shared system devices for non-root users in compositors; this includes graphics cards. For traditional X11 managers, `seatd` is not needed, such as both Plasma and GNOME, but for the Wayland compositors discussed here, it will need enabled on the system and be running before starting a compositor environment. To enable and start the `seatd` daemon now, and on system initialization: [source,shell] ---- # sysrc seatd_enable="YES" # service seatd start ---- Afterward, a compositor, which is similar to an X11 desktop, will need to be installed for the GUI environment. Three are discussed here, including basic configuration options, setting up a screen lock, and recommendations for more information. [[wayland-wayfire]] == The Wayfire Compositor Wayfire is a compositor that aims to be lightweight and customizable. Several features are available, and it brings back several elements from the previously released Compiz Fusion desktop. All of the parts look beautiful on modern hardware. To get Wayfire up and running, begin by installing the required packages: [source,shell] ---- # pkg install wayfire wf-shell alacritty swaylock-effects swayidle wlogout kanshi mako wlsunset ---- The `alacritty` package provides a terminal emulator. Still, it is not completely required as other terminal emulators such as `kitty`, and XFCE-4 `Terminal` have been tested and verified to work under the Wayfire compositor. Wayfire configuration is relatively simple; it uses a file that should be reviewed for any customizations. To begin, copy the example file over to the runtime environment configuration directory and then edit the file: [source,shell] ---- % mkdir ~/.config/wayfire % cp /usr/local/share/examples/wayfire/wayfire.ini ~/.config/wayfire ---- The defaults for most users should be fine. Within the configuration file, items like the famous `cube` are pre-configured, and there are instructions to help with the available settings. A few primary settings of note include: [.programlisting] .... [output] mode = 1920x1080@60000 position = 0,0 transform = normal scale = 1.000000 .... In this example, from the configuration file, the screen's output should be the listed mode at the listed hertz. For example, the mode should be set to `widthxheight@refresh_rate`. The position places the output at a specific pixel location specified. The default should be fine for most users. Finally, transform sets a background transform, and scale will scale the output to the specified scale factor. The defaults for these options are generally acceptable; for more information, see the documentation. As mentioned, Wayland is new, and not all applications work with the protocol yet. At this time, `sddm` does not appear to support starting and managing compositors in Wayland. The `swaylock` utility has been used instead in these examples. The configuration file contains options to run `swayidle` and `swaylock` for idle and locking of the screen. This option to define the action to take when the system is idle is listed as: [.programlisting] .... idle = swaylock .... And the lock timeout is configured using the following lines: [.programlisting] .... [idle] toggle = KEY_Z screensaver_timeout = 300 dpms_timeout = 600 .... The first option will lock the screen after 300 seconds, and after another 300, the screen will shut off through the `dpms_timeout` option. One final thing to note is the key. Most of the configuration mentions this key, and it is the traditional `Windows` key on the keyboard. Most keyboards have this super key available; however, it should be remapped within this configuration file if it is not available. For example, to lock the screen, press and hold the super key, the kbd:[shift] key, and press the kbd:[escape] key. Unless the mappings have changed, this will execute the swaylock application. The default configuration for `swaylock` will show a grey screen; however, the application is highly customizable and well documented. In addition, since the swaylock-effects is the version that was installed, there are several options available such as the blur effect, which can be seen using the following command: [source,shell] ---- % swaylock --effect-blur 7x5 ---- There is also the `--clock` parameter which will display a clock with the date and time on the lock screen. When package:x11/swaylock-effects[] was installed, a default [.filename]#pam.d# configuration was included. It provides the default options that should be fine for most users. More advanced options are available; see the PAM documentation for more information. At this point, it is time to test Wayfire and see if it can start up on the system. Just type the following command: [source,shell] ---- % wayfire -c ~/.config/wayfire/wayfire.ini ---- The compositor should now start and display a background image along with a menu bar at the top of the screen. Wayfire will attempt to list installed compatible applications for the desktop and present them in this drop-down menu; for example, if the XFCE-4 file manager is installed, it will show up in this drop-down menu. If a specific application is compatible and valuable enough for a keyboard shortcut, it may be mapped to a keyboard sequence using the [.filename]#wayfire.ini# configuration file. Wayfire also has a configuration tool named Wayfire Config Manager. It is located in the drop-down menu bar but may also be started through a terminal by issuing the following command: [source,shell] ---- % wcm ---- Various Wayfire configuration options, including the composite special effects, maybe enabled, disabled, or configured through this application. In addition, for a more user-friendly experience, a background manager, panel, and docking application may be enabled in the configuration file: [.programlisting] .... panel = wf-panel dock = wf-dock background = wf-background .... [WARNING] ==== Changes made through `wcm` will overwrite custom changes in the [.filename]#wayfire.ini# configuration file. The [.filename]#wayfire.ini# file is highly recommended to be backed up so any essential changes may be restored. ==== Finally, the default launcher listed in the [.filename]#wayfire.ini# is package:x11/wf-shell[] which may be replaced with other panels if desired by the user. [[wayland-hikari]] == The Hikari Compositor The Hikari compositor uses several concepts centered around productivity, such as sheets, workspaces, and more. In that way, it resembles a tiling window manager. Breaking this down, the compositor starts with a single workspace, which is similar to virtual desktops. Hikari uses a single workspace or virtual desktop for user interaction. The workspace is made up of several views, which are the working windows in the compositor grouped as either sheets or groups. Both sheets and groups are made up of a collection of views; again, the windows that are grouped together. When switching between sheets or groups, the active sheet or group will become known collectively as the workspace. The manual page will break this down into more information on the functions of each but for this document, just consider a single workspace utilizing a single sheet. Hikari installation will comprise of a single package, package:x11-wm/hikari[], and a terminal emulator `alacritty`: [source,shell] ---- # pkg install hikari alacritty ---- [NOTE] ==== Other shells, such as `kitty` or the Plasma `Terminal`, will function under Wayland. Users should experiment with their favorite terminal editor to validate compatibility. ==== Hikari uses a configuration file, [.filename]#hikari.conf#, which could either be placed in the XDG_RUNTIME_DIR or specified on startup using the `-c` parameter. An autostart configuration file is not required but may make the migration to this compositor a little easier. Beginning the configuration is to create the Hikari configuration directory and copy over the configuration file for editing: [source,shell] ---- % mkdir ~/.config/hikari % cp /usr/local/etc/hikari/hikari.conf ~/.config/hikari ---- The configuration is broken out into various stanzas such as ui, outputs, layouts, and more. For most users, the defaults will function fine; however, some important changes should be made. For example, the $TERMINAL variable is normally not set within the user's environment. Changing this variable or altering the [.filename]#hikari.conf# file to read: [.programlisting] .... terminal = "/usr/local/bin/alacritty" .... Will launch the `alacritty` terminal using the bound key press. While going through the configuration file, it should be noted that the capital letters are used to map keys out for the user. For example, the kbd:[L] key for starting the terminal kbd:[L+Return] is actually the previously discussed super key or Windows logo key. Therefore, holding the kbd:[L/super/Windows] key and pressing kbd:[Enter] will open the specified terminal emulator with the default configuration. Mapping other keys to applications require an action definition to be created. For this, the action item should be listed in the actions stanza, for example: [.programlisting] .... actions { terminal = "/usr/local/bin/alacritty" browser = "/usr/local/bin/firefox" } .... Then an action may be mapped under the keyboard stanza, which is defined within the bindings stanza: [.programlisting] .... bindings { keyboard { SNIP "L+Return" = action-terminal "L+b" = action-browser SNIP .... After Hikari is restarted, holding the Windows logo button and pressing the kbd:[b] key on the keyboard will start the web browser. The compositor does not have a menu bar, and it is recommended the user set up, at minimal, a terminal emulator before migration. The manual page contains a great deal of documentation it should be read before performing a full migration. Another positive aspect about Hikari is that, while migrating to the compositor, Hikari can be started in the Plasma and GNOME desktop environments, allowing for a test-drive before completely migrating. Locking the screen in Hikari is easy because a default [.filename]#pam.d# configuration file and unlock utility are bundled with the package. The key binding for locking the screen is kbd:[L] (Windows logo key)+ kbd:[Shift] + kbd:[Backspace]. It should be noted that all views not marked public will be hidden. These views will never accept input when locked but beware of sensitive information being visible. For some users, it may be easier to migrate to a different screen locking utility such as swaylock-effects, discussed in this section. To start Hikari, use the following command: [source,shell] ---- % hikari -c ~/.config/hikari/hikari.conf ---- [[wayland-sway]] == The Sway Compositor The Sway compositor is a tiling compositor that attempts to replace the i3 windows manager. It should work with the user's current i3 configuration; however, new features may require some additional setup. Before starting the Sway installation, ensure that a graphics card (GPU) is installed and configured correctly. Refer to the section crossref:x11[x-graphic-card-drivers,drivers for the graphics hardware] for guidance. This step is essential for the Sway compositor to function properly. In the forthcoming examples, a fresh installation without migrating any i3 configuration will be assumed. To install Sway and valuable components, issue the following command as the root user: [source,shell] ---- # pkg install sway swayidle swaylock-effects alacritty dmenu-wayland dmenu ---- For a basic configuration file, issue the following commands and then edit the configuration file after it is copied: [source,shell] ---- % mkdir ~/.config/sway % cp /usr/local/etc/sway/config ~/.config/sway ---- The base configuration file has many defaults, which will be fine for most users. Several important changes should be made like the following: [.programlisting] .... # Logo key. Use Mod1 for Alt. input * xkb_rules evdev set $mod Mod4 # The preferred terminal emulator set $term alacritty set $lock swaylock -f -c 000000 output "My Workstation" mode 1366x768@60Hz position 1366 0 output * bg ~/wallpapers/mywallpaper.png stretch ### Idle configuration exec swayidle -w \ timeout 300 'swaylock -f -c 000000' \ timeout 600 'swaymsg "output * dpms off"' resume 'swaymsg "output * dpms on"' \ before-sleep 'swaylock -f -c 000000' .... In the previous example, the `xkb` rules for man:evdev[4] events are loaded, and the $mod key is set to the Windows logo key for the key bindings. Next, the terminal emulator was set to be `alacritty`, and a screen lock command was defined; more on this later. The output keyword, the mode, the position, a background wallpaper, and Sway is also told to stretch this wallpaper to fill out the screen. Finally, `swayidle` is set to daemonize and lock the screen after a timeout of 300 seconds, placing the screen or monitor into sleep mode after 600 seconds. The locked background color of 000000, which is black, is also defined here. Using swaylock-effects, a clock may also be displayed with the `--clock` parameter. See the manual page for more options. The man:sway-output[5] manual page should also be reviewed; it includes a great deal of information on customing the output options available. While in Sway, to bring up a menu of applications, hold the Windows logo key (mod) and press the kbd:[d] key. The menu may be navigated using the arrow keys on the keyboard. There is also a method to manipulate the layout of the bar and add a tray; read the man:sway-bar[5] manual page for more information. The default configuration adds a date and time to the upper right-hand corner. See the `Bar` stanza in the configuration file for an example. By default, the configuration does not include locking the screen outside of the example above, enabling a lockout timer. Creating a lock key binding requires the following line to the `Key bindings` section: [.programlising] .... # Lock the screen manually bindsym $mod+Shift+Return exec $lock .... Now the screen may be locked using the combination of holding the Windows logo key, pressing and holding shift, and finally pressing return. When Sway is installed, whether from a package or the FreeBSD Ports Collection, a default file for [.filename]#pam.d# was installed. The default configuration should be acceptable for most users, but more advanced options are available. Read through the PAM documentation for more information. Finally, to exit Sway and return to the shell, hold the Windows logo key, the shift key, and press the kbd:[e] key. A prompt will be displayed with an option to exit Sway. During migration, Sway can be started through a terminal emulator on an X11 desktop such as Plasma. This makes testing different changes and key bindings a little easier prior to fully migrating to this compositor. To start Sway, issue the following command: [source,shell] ---- % sway -c ~/.config/sway/config ---- [[wayland-xwayland]] == Using Xwayland When installing Wayland, the `Xwayland` binary should have been installed unless Wayland was built without X11 support. If the [.filename]#/usr/local/bin/Xwayland# file does not exist, install it using the following command: [source,shell] ---- # pkg install xwayland ---- [NOTE] ==== The development version of Xwayland is recommended and was most likely installed with the Wayland package. Each compositor has a method of enabling or disabling this feature. ==== Once `Xwayland` has been installed, configure it within the chosen compositor. For Wayfire, the following line is required in the [.filename]#wayfire.ini# file: [.programlisting] .... xwayland = true .... For the Sway compositor, `Xwayland` should be enabled by default. Even so, it is recommended to manually add a configuration line in the [.filename]#~/.config/sway/config# like the following: [.programlisting] ..... xwayland enable ..... Finally, for Hikari, no changes are needed. Support for `Xwayland` is built in by default. To disable that support, rebuild the package from the ports collection and disable Xwayland support at that time. After these changes are made, start the compositor at the command line and execute a terminal from the key bindings. Within this terminal, issue the `env` command and search for the `DISPLAY` variables. If the compositor was able to properly start the Xwayland X server, these environment variables should look similar to the following: [source,shell] ---- % env | grep DISPLAY ---- [.programlisting] .... WAYLAND_DISPLAY=wayland-1 DISPLAY=:0 .... In this output, there is a default Wayland display and a display set for the Xwayland server. Another method to verify that `Xwayland` is functioning properly is to use install and test the small package:[x11/eyes] and check the output. If the `xeyes` application starts and the eyes follow the mouse pointer, Xwayland is functioning properly. If an error such as the following is displayed, something happened during the `Xwayland` initialization and it may need reinstalled: [.programlisting] .... Error: Cannot open display wayland-0 .... [WARNING] ==== A security feature of Wayland is that, without running an X server, there is not another network listener. Once `Xwayland` is enabled, this security feature is no longer applicable to the system. ==== For some compositors, such as Wayfire, `Xwayland` may not start properly. As such, `env` will show the following information for the `DISPLAY` environment variables: [source,shell] ---- % env | grep DISPLAY ---- [.programlisting] .... DISPLAY=wayland-1 WAYLAND_DISPLAY=wayland-1 .... Even though `Xwayfire` was installed and configured, X11 applications will not start giving a display issue. To work around this, verify that there is already an instance of `Xwayland` using a UNIX socket through these two methods. First, check the output from `sockstat` and search for X11-unix: [source,shell] ---- % sockstat | grep x11 ---- There should be something similar to the following information: [.programlisting] .... trhodes Xwayland 2734 8 stream /tmp/.X11-unix/X0 trhodes Xwayland 2734 9 stream /tmp/.X11-unix/X0 trhodes Xwayland 2734 10 stream /tmp/.X11-unix/X0 trhodes Xwayland 2734 27 stream /tmp/.X11-unix/X0_ trhodes Xwayland 2734 28 stream /tmp/.X11-unix/X0 .... This suggests the existence of an X11 socket. This can be further verified by attempting to execute `Xwayland` manually within a terminal emulator running under the compositor: [source,shell] ---- % Xwayland ---- If an X11 socket is already available, the following error should be presented to the user: [.programlisting] .... (EE) Fatal server error: (EE) Server is already active for display 0 If this server is no longer running, remove /tmp/.X0-lock and start again. (EE) .... Since there is an active X display available using display zero, the environment variable was just set improperly, to fix this, change the `DISPLAY` environment variable to `:0` and attempt to execute the application again. The following example uses package:mail/claws-mail[] as the application which needs the `Xwayland` service: [source,shell] ---- export DISPLAY=:0 ---- After this change, the package:mail/claws-mail[] application should now start using `Xwayland` and function as expected. [[wayland-remotedesktop]] == Remote Desktop Using VNC Earlier in this document it was noted that Wayland does not provide the same X server style access as Xorg provides. Instead, users are free to pick and choose a remote desktop protocol such as RDP or VNC. The FreeBSD Ports collection includes the `wayvnc`, which will support wlroots based compositors such as the ones discussed here. This application may be installed using: [source,shell] ---- # pkg install wayvnc ---- Unlike some other packages, `wayvnc` does not come with a configuration file. Thankfully, the manual page documents the important options and they may be extrapolated into a simple configuration file: [.programlisting] .... address=0.0.0.0 enable_auth=true username=username password=password private_key_file=/path/to/key.pem certificate_file=/path/to/cert.pem .... The key files will need to be generated, and it is highly recommended they be used for increased security of the connection. When invoked, wayvnc will search for the configuration file in [.filename]#~/.config/wayvnc/config#. This could be overwritten using the `-C configuration_file` option when starting the server. Thus, to start the `wayvnc` server, issue the following command: [source,shell] ---- % wayvnc -C ~/.config/wayvnc/config ---- [NOTE] ==== At the time of this writing, there is no rc.d script to start `wayvnc` on system initialization. If that functionality is desired, a local startup file will need to be created. This is probably a feature request for the port maintainer. ==== [[wayland-ly]] == Wayland Login Manager While several login managers exist and are slowly migrating to Wayland, one option is the package:x11/ly[] text user interface (TUI) manager. Needing minimal configuration, `ly` will start Sway, Wayfire, and others by presenting a login window on system initialization. To install `ly`, issue the following command: [source,shell] ---- # pkg install ly ---- There will be some configuration hints presented, the import steps are to add the following lines to [.filename]#/etc/gettytab#: -[programlisting] +[.programlisting] .... Ly:\ :lo=/usr/local/bin/ly:\ :al=root: .... And then modify the ttyv1 line in [.filename]#/etc/ttys# to match the following line: -[programlisting] +[.programlisting] .... ttyv1 "/usr/libexec/getty Ly" xterm onifexists secure .... After a system reboot, a login should appear. To configure specific settings, such as language and edit [.filename]#/usr/local/etc/ly/config.ini#. At minimal, this file should have the designated tty that was previously specified in [.filename]#/etc/ttys#. [NOTE] ==== If setting ttyv0 up as the login terminal, it may be required to press the kbd:[alt] and kbd:[F1] keys to properly see the login window. ==== When the login window appears, using the left and right arrows will swap through different, supported, window managers. [[wayland-utilities]] == Useful Utilities One useful Wayland utility which all compositors can make use of is the waybar. While Wayfire does come with a launch menu, an easy-to-use and fast taskbar is a good accessory for any compositor or desktop manager. A Wayland compatible taskbar that is fast and easy to configure is waybar. To install the package and a supporting audio control utility, issue the following command: [source,shell] ---- # pkg install pavucontrol waybar ---- To create the configuration directory and copy over a default configuration file, execute the following commands: [source,shell] ---- % mkdir ~/.config/waybar % cp /usr/local/etc/xdg/waybar/config ~/.config/waybar ---- The `lavalauncher` utility provides a launch bar for various applications. There is no example configuration file provided with the package, so the following actions must be taken: [source,shell] ---- mkdir ~/.config/lavalauncher ---- An example configuration file that only includes Firefox, and is placed on the right, is below: [.programlising] .... global-settings { watch-config-file = true; } bar { output = eDP-1; position = bottom; background-colour = "#202020"; # Condition for the default configuration set. condition-resolution = wider-than-high; config { position = right; } button { image-path = /usr/local/lib/firefox/browser/chrome/icons/default/default48.png; command[mouse-left] = /usr/local/bin/firefox; } button { image-path = /usr/local/share/pixmaps/thunderbird.png; command[mouse-left] = /usr/local/bin/thunderbird; } .... diff --git a/documentation/content/en/books/porters-handbook/makefiles/_index.adoc b/documentation/content/en/books/porters-handbook/makefiles/_index.adoc index 8415ede9a4..a9ffe801b9 100644 --- a/documentation/content/en/books/porters-handbook/makefiles/_index.adoc +++ b/documentation/content/en/books/porters-handbook/makefiles/_index.adoc @@ -1,5450 +1,5450 @@ --- title: Chapter 5. Configuring the Makefile prev: books/porters-handbook/slow-porting next: books/porters-handbook/special description: Configuring the Makefile for FreeBSD Ports tags: ["makefiles", "configuring", "naming", "versions"] showBookMenu: true weight: 5 params: path: "/books/porters-handbook/makefiles/" --- [[makefiles]] = Configuring the Makefile :doctype: book :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumoffset: 5 :partnums: :source-highlighter: rouge :experimental: :g-plus-plus: g++ :images-path: books/porters-handbook/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] Configuring the [.filename]#Makefile# is pretty simple, and again we suggest looking at existing examples before starting. Also, there is a crossref:porting-samplem[porting-samplem,sample Makefile] in this handbook, so take a look and please follow the ordering of variables and sections in that template to make the port easier for others to read. Consider these problems in sequence during the design of the new [.filename]#Makefile#: [[makefile-source]] == The Original Source Does it live in `DISTDIR` as a standard ``gzip``ped tarball named something like [.filename]#foozolix-1.2.tar.gz#? If so, go on to the next step. If not, the distribution file format might require overriding one or more of `DISTVERSION`, `DISTNAME`, `EXTRACT_CMD`, `EXTRACT_BEFORE_ARGS`, `EXTRACT_AFTER_ARGS`, `EXTRACT_SUFX`, or `DISTFILES`. In the worst case, create a custom `do-extract` target to override the default. This is rarely, if ever, necessary. [[makefile-naming]] == Naming The first part of the port's [.filename]#Makefile# names the port, describes its version number, and lists it in the correct category. [[makefile-portname]] === `PORTNAME` Set `PORTNAME` to the base name of the software. It is used as the base for the FreeBSD package, and for crossref:makefiles[makefile-distname,`DISTNAME`]. [IMPORTANT] ==== The package name must be unique across the entire ports tree. Make sure that the `PORTNAME` is not already in use by an existing port, and that no other port already has the same `PKGBASE`. If the name has already been used, add either crossref:makefiles[porting-pkgnameprefix-suffix,`PKGNAMEPREFIX` or `PKGNAMESUFFIX`]. ==== [[makefile-versions]] === Versions, `DISTVERSION` _or_ `PORTVERSION` Set `DISTVERSION` to the version number of the software. `PORTVERSION` is the version used for the FreeBSD package. It will be automatically derived from `DISTVERSION` to be compatible with FreeBSD's package versioning scheme. If the version contains _letters_, it might be needed to set `PORTVERSION` and not `DISTVERSION`. [IMPORTANT] ==== Only one of `PORTVERSION` and `DISTVERSION` can be set at a time. ==== From time to time, some software will use a version scheme that is not compatible with how `DISTVERSION` translates in `PORTVERSION`. [TIP] ==== When updating a port, it is possible to use the `-t` argument of man:pkg-version[8] to check if the new version is greater or lesser than before. See below on how to use man:pkg-version[8] to compare versions. ==== [[makefile-versions-ex-pkg-version]] .Using man:pkg-version[8] to Compare Versions [example] ==== `pkg version -t` takes two versions as arguments, it will respond with `<`, `=` or `>` if the first version is less, equal, or more than the second version, respectively. [source,shell] .... % pkg version -t 1.2 1.3 < <.> % pkg version -t 1.2 1.2 = <.> % pkg version -t 1.2 1.2.0 = <.> % pkg version -t 1.2 1.2.p1 > <.> % pkg version -t 1.2.a1 1.2.b1 < <.> % pkg version -t 1.2 1.2p1 < <.> .... <.> `1.2` is before `1.3`. <.> `1.2` and `1.2` are equal as they have the same version. <.> `1.2` and `1.2.0` are equal as nothing equals zero. <.> `1.2` is after `1.2.p1` as `.p1`, think "pre-release 1". <.> `1.2.a1` is before `1.2.b1`, think "alpha" and "beta", and `a` is before `b`. <.> `1.2` is before `1.2p1` as `2p1`, think "2, patch level 1" which is a version after any `2.X` but before `3`. [NOTE] -**** +===== In here, the `a`, `b`, and `p` are used as if meaning "alpha", "beta" or "pre-release" and "patch level", but they are only letters and are sorted alphabetically, so any letter can be used, and they will be sorted appropriately. -**** +===== ==== .Examples of `DISTVERSION` and the Derived `PORTVERSION` [cols="10%,90%", frame="none", options="header"] |=== | DISTVERSION | PORTVERSION |0.7.1d |0.7.1.d |10Alpha3 |10.a3 |3Beta7-pre2 |3.b7.p2 |8:f_17 |8f.17 |=== [[makefile-versions-ex1]] .Using `DISTVERSION` [example] ==== When the version only contains numbers separated by dots, dashes or underscores, use `DISTVERSION`. [.programlisting] .... PORTNAME= nekoto DISTVERSION= 1.2-4 .... It will generate a `PORTVERSION` of `1.2.4`. ==== [[makefile-versions-ex2]] .Using `DISTVERSION` When the Version Starts with a Letter or a Prefix [example] ==== When the version starts or ends with a letter, or a prefix or a suffix that is not part of the version, use `DISTVERSIONPREFIX`, `DISTVERSION`, and `DISTVERSIONSUFFIX`. If the version is `v1.2-4`: [.programlisting] .... PORTNAME= nekoto DISTVERSIONPREFIX= v DISTVERSION= 1_2_4 .... Some of the time, projects using GitHub will use their name in their versions. For example, the version could be `nekoto-1.2-4`: [.programlisting] .... PORTNAME= nekoto DISTVERSIONPREFIX= nekoto- DISTVERSION= 1.2_4 .... Those projects also sometimes use some string at the end of the version, for example, `1.2-4_RELEASE`: [.programlisting] .... PORTNAME= nekoto DISTVERSION= 1.2-4 DISTVERSIONSUFFIX= _RELEASE .... Or they do both, for example, `nekoto-1.2-4_RELEASE`: [.programlisting] .... PORTNAME= nekoto DISTVERSIONPREFIX= nekoto- DISTVERSION= 1.2-4 DISTVERSIONSUFFIX= _RELEASE .... `DISTVERSIONPREFIX` and `DISTVERSIONSUFFIX` will not be used while constructing `PORTVERSION`, but only used in `DISTNAME`. All will generate a `PORTVERSION` of `1.2.4`. ==== [[makefile-versions-ex3]] .Using `DISTVERSION` When the Version Contains Letters Meaning "alpha", "beta", or "pre-release" [example] ==== When the version contains numbers separated by dots, dashes or underscores, and letters are used to mean "alpha", "beta" or "pre-release", which is, before the version without the letters, use `DISTVERSION`. [.programlisting] .... PORTNAME= nekoto DISTVERSION= 1.2-pre4 .... [.programlisting] .... PORTNAME= nekoto DISTVERSION= 1.2p4 .... Both will generate a `PORTVERSION` of `1.2.p4` which is before than 1.2. man:pkg-version[8] can be used to check that fact: [source,shell] .... % pkg version -t 1.2.p4 1.2 < .... ==== [[makefile-versions-ex4]] .Not Using `DISTVERSION` When the Version Contains Letters Meaning "Patch Level" [example] ==== When the version contains letters that are not meant as "alpha", "beta", or "pre", but more in a "patch level", and meaning after the version without the letters, use `PORTVERSION`. [.programlisting] .... PORTNAME= nekoto PORTVERSION= 1.2p4 .... In this case, using `DISTVERSION` is not possible because it would generate a version of `1.2.p4` which would be before `1.2` and not after. man:pkg-version[8] will verify this: [source,shell] .... % pkg version -t 1.2 1.2.p4 > <.> % pkg version -t 1.2 1.2p4 < <.> .... <.> `1.2` is after `1.2.p4`, which is _wrong_ in this case. <.> `1.2` is before `1.2p4`, which is what was needed. ==== For some more advanced examples of setting `PORTVERSION`, when the software's versioning is really not compatible with FreeBSD's, or `DISTNAME` when the distribution file does not contain the version itself, see crossref:makefiles[makefile-distname, `DISTNAME`]. [[makefile-naming-revepoch]] === `PORTREVISION` and `PORTEPOCH` [[makefile-portrevision]] ==== `PORTREVISION` `PORTREVISION` is a monotonically increasing value which is reset to 0 with every increase of `DISTVERSION`, typically every time there is a new official vendor release. If `PORTREVISION` is non-zero, the value is appended to the package name. Changes to `PORTREVISION` are used by automated tools like man:pkg-version[8] to determine that a new package is available. `PORTREVISION` must be increased each time a change is made to the port that changes the generated package in any way. That includes changes that only affect a package built with non-default crossref:makefiles[makefile-options,options]. Examples of when `PORTREVISION` must be bumped: * Addition of patches to correct security vulnerabilities, bugs, or to add new functionality to the port. * Changes to the port [.filename]#Makefile# to enable or disable compile-time options in the package. * Changes in the packing list or the install-time behavior of the package. For example, a change to a script which generates initial data for the package, like man:ssh[1] host keys. * Version bump of a port's shared library dependency (in this case, someone trying to install the old package after installing a newer version of the dependency will fail since it will look for the old libfoo.x instead of libfoo.(x+1)). * Silent changes to the port distfile which have significant functional differences. For example, changes to the distfile requiring a correction to [.filename]#distinfo# with no corresponding change to `DISTVERSION`, where a `diff -ru` of the old and new versions shows non-trivial changes to the code. * Changes to `MAINTAINER`. Examples of changes which do not require a `PORTREVISION` bump: * Style changes to the port skeleton with no functional change to what appears in the resulting package. * Changes to `MASTER_SITES` or other functional changes to the port which do not affect the resulting package. * Trivial patches to the distfile such as correction of typos, which are not important enough that users of the package have to go to the trouble of upgrading. * Build fixes which cause a package to become compilable where it was previously failing. As long as the changes do not introduce any functional change on any other platforms on which the port did previously build. Since `PORTREVISION` reflects the content of the package, if the package was not previously buildable then there is no need to increase `PORTREVISION` to mark a change. A rule of thumb is to decide whether a change committed to a port is something which _some_ people would benefit from having. Either because of an enhancement, fix, or by virtue that the new package will actually work at all. Then weigh that against that fact that it will cause everyone who regularly updates their ports tree to be compelled to update. If yes, `PORTREVISION` must be bumped. [NOTE] ==== People using binary packages will _never_ see the update if `PORTREVISION` is not bumped. Without increasing `PORTREVISION`, the package builders have no way to detect the change and thus, will not rebuild the package. ==== [[makefile-portepoch]] ==== `PORTEPOCH` From time to time a software vendor or FreeBSD porter will do something silly and release a version of their software which is actually numerically less than the previous version. An example of this is a port which goes from foo-20000801 to foo-1.0 (the former will be incorrectly treated as a newer version since 20000801 is a numerically greater value than 1). [TIP] ==== The results of version number comparisons are not always obvious. `pkg version` (see man:pkg-version[8]) can be used to test the comparison of two version number strings. For example: [source,shell] .... % pkg version -t 0.031 0.29 > .... The `>` output indicates that version 0.031 is considered greater than version 0.29, which may not have been obvious to the porter. ==== In situations such as this, `PORTEPOCH` must be increased. If `PORTEPOCH` is nonzero it is appended to the package name as described in section 0 above. `PORTEPOCH` must never be decreased or reset to zero, because that would cause comparison to a package from an earlier epoch to fail. For example, the package would not be detected as out of date. The new version number, `1.0,1` in the above example, is still numerically less than the previous version, 20000801, but the `,1` suffix is treated specially by automated tools and found to be greater than the implied suffix `,0` on the earlier package. Dropping or resetting `PORTEPOCH` incorrectly leads to no end of grief. If the discussion above was not clear enough, please consult the {freebsd-ports}. It is expected that `PORTEPOCH` will not be used for the majority of ports, and that sensible use of `DISTVERSION`, or that use `PORTVERSION` carefully, can often preempt it becoming necessary if a future release of the software changes the version structure. However, care is needed by FreeBSD porters when a vendor release is made without an official version number - such as a code "snapshot" release. The temptation is to label the release with the release date, which will cause problems as in the example above when a new "official" release is made. For example, if a snapshot release is made on the date `20000917`, and the previous version of the software was version `1.2`, do not use `20000917` for `DISTVERSION`. The correct way is a `DISTVERSION` of `1.2.20000917`, or similar, so that the succeeding release, say `1.3`, is still a numerically greater value. [[makefile-portrevision-example]] ==== Example of `PORTREVISION` and `PORTEPOCH` Usage The `gtkmumble` port, version `0.10`, is committed to the ports collection: [.programlisting] .... PORTNAME= gtkmumble DISTVERSION= 0.10 .... `PKGNAME` becomes `gtkmumble-0.10`. A security hole is discovered which requires a local FreeBSD patch. `PORTREVISION` is bumped accordingly. [.programlisting] .... PORTNAME= gtkmumble DISTVERSION= 0.10 PORTREVISION= 1 .... `PKGNAME` becomes `gtkmumble-0.10_1` A new version is released by the vendor, numbered `0.2` (it turns out the author actually intended `0.10` to actually mean `0.1.0`, not "what comes after 0.9" - oops, too late now). Since the new minor version `2` is numerically less than the previous version `10`, `PORTEPOCH` must be bumped to manually force the new package to be detected as "newer". Since it is a new vendor release of the code, `PORTREVISION` is reset to 0 (or removed from the [.filename]#Makefile#). [.programlisting] .... PORTNAME= gtkmumble DISTVERSION= 0.2 PORTEPOCH= 1 .... `PKGNAME` becomes `gtkmumble-0.2,1` The next release is 0.3. Since `PORTEPOCH` never decreases, the version variables are now: [.programlisting] .... PORTNAME= gtkmumble DISTVERSION= 0.3 PORTEPOCH= 1 .... `PKGNAME` becomes `gtkmumble-0.3,1` [NOTE] ==== If `PORTEPOCH` were reset to `0` with this upgrade, someone who had installed the `gtkmumble-0.10_1` package would not detect the `gtkmumble-0.3` package as newer, since `3` is still numerically less than `10`. Remember, this is the whole point of `PORTEPOCH` in the first place. ==== [[porting-pkgnameprefix-suffix]] === `PKGNAMEPREFIX` and `PKGNAMESUFFIX` Two optional variables, `PKGNAMEPREFIX` and `PKGNAMESUFFIX`, are combined with `PORTNAME` and `PORTVERSION` to form `PKGNAME` as `${PKGNAMEPREFIX}${PORTNAME}${PKGNAMESUFFIX}-${PORTVERSION}`. Make sure this conforms to our crossref:makefiles[porting-pkgname,guidelines for a good package name]. In particular, the use of a hyphen (`-`) in `PORTVERSION` is _not_ allowed. Also, if the package name has the _language-_ or the _-compiled.specifics_ part (see below), use `PKGNAMEPREFIX` and `PKGNAMESUFFIX`, respectively. Do not make them part of `PORTNAME`. [[porting-pkgname]] === Package Naming Conventions These are the conventions to follow when naming packages. This is to make the package directory easy to scan, as there are already thousands of packages and users are going to turn away if they hurt their eyes! Package names take the form of [.filename]#language_region-name-compiled.specifics-version.numbers#. The package name is defined as `${PKGNAMEPREFIX}${PORTNAME}${PKGNAMESUFFIX}-${PORTVERSION}`. Make sure to set the variables to conform to that format. [[porting-pkgname-language]] [.filename]#language_region-#:: FreeBSD strives to support the native language of its users. The _language-_ part is a two letter abbreviation of the natural language defined by ISO-639 when the port is specific to a certain language. Examples are `ja` for Japanese, `ru` for Russian, `vi` for Vietnamese, `zh` for Chinese, `ko` for Korean and `de` for German. + If the port is specific to a certain region within the language area, add the two letter country code as well. Examples are `en_US` for US English and `fr_CH` for Swiss French. + The _language-_ part is set in `PKGNAMEPREFIX`. [[porting-pkgname-name]] [.filename]#name#:: Make sure that the port's name and version are clearly separated and placed into `PORTNAME` and `DISTVERSION`. The only reason for `PORTNAME` to contain a version part is if the upstream distribution is really named that way, as in the package:textproc/libxml2[] or package:japanese/kinput2-freewnn[] ports. Otherwise, `PORTNAME` cannot contain any version-specific information. It is quite normal for several ports to have the same `PORTNAME`, as the package:www/apache*[] ports do; in that case, different versions (and different index entries) are distinguished by `PKGNAMEPREFIX` and `PKGNAMESUFFIX` values. + There is a tradition of naming `Perl 5` modules by prepending `p5-` and converting the double-colon separator to a hyphen. For example, the `Data::Dumper` module becomes `p5-Data-Dumper`. [[porting-pkgname-compiled-specifics]] [.filename]#-compiled.specifics#:: If the port can be built with different crossref:makefiles[makefile-masterdir,hardcoded defaults] (usually part of the directory name in a family of ports), the _-compiled.specifics_ part states the compiled-in defaults. The hyphen is optional. Examples are paper size and font units. + The _-compiled.specifics_ part is set in `PKGNAMESUFFIX`. [[porting-pkgname-version-numbers]] [.filename]#-version.numbers#:: The version string follows a dash (`-`) and is a period-separated list of integers and single lowercase alphabetics. In particular, it is not permissible to have another dash inside the version string. The only exception is the string `pl` (meaning "patchlevel"), which can be used _only_ when there are no major and minor version numbers in the software. If the software version has strings like "alpha", "beta", "rc", or "pre", take the first letter and put it immediately after a period. If the version string continues after those names, the numbers follow the single alphabet without an extra period between them (for example, `1.0b2`). + The idea is to make it easier to sort ports by looking at the version string. In particular, make sure version number components are always delimited by a period, and if the date is part of the string, use the `d__yyyy.mm.dd__` format, not `_dd.mm.yyyy_` or the non-Y2K compliant `_yy.mm.dd_` format. It is important to prefix the version with a letter, here `d` (for date), in case a release with an actual version number is made, which would be numerically less than `_yyyy_`. [IMPORTANT] ==== Package name must be unique among all of the ports tree, check that there is not already a port with the same `PORTNAME` and if there is add one of crossref:makefiles[porting-pkgnameprefix-suffix,`PKGNAMEPREFIX` or `PKGNAMESUFFIX`]. ==== Here are some (real) examples on how to convert the name as called by the software authors to a suitable package name, for each line, only one of `DISTVERSION` or `PORTVERSION` is set in, depending on which would be used in the port's [.filename]#Makefile#: .Package Naming Examples [cols="1,1,1,1,1,1,1", frame="none", options="header"] |=== | Distribution Name | PKGNAMEPREFIX | PORTNAME | PKGNAMESUFFIX | DISTVERSION | PORTVERSION | Reason or comment |mule-2.2.2 |(empty) |mule |(empty) |2.2.2 | |No changes required |mule-1.0.1 |(empty) |mule |1 |1.0.1 | |This is version 1 of mule, and version 2 already exists |EmiClock-1.0.2 |(empty) |emiclock |(empty) |1.0.2 | |No uppercase names for single programs |rdist-1.3alpha |(empty) |rdist |(empty) |1.3alpha | |Version will be `1.3.a` |es-0.9-beta1 |(empty) |es |(empty) |0.9-beta1 | |Version will be `0.9.b1` |mailman-2.0rc3 |(empty) |mailman |(empty) |2.0rc3 | |Version will be `2.0.r3` |v3.3beta021.src |(empty) |tiff |(empty) | |3.3 |What the heck was that anyway? |tvtwm |(empty) |tvtwm |(empty) | |p11 |No version in the filename, use what upstream says it is |piewm |(empty) |piewm |(empty) |1.0 | |No version in the filename, use what upstream says it is |xvgr-2.10pl1 |(empty) |xvgr |(empty) | |2.10.pl1 |In that case, `pl1` means patch level, so using DISTVERSION is not possible. |gawk-2.15.6 |ja- |gawk |(empty) |2.15.6 | |Japanese language version |psutils-1.13 |(empty) |psutils |-letter |1.13 | |Paper size hardcoded at package build time |pkfonts |(empty) |pkfonts |300 |1.0 | |Package for 300dpi fonts |=== If there is absolutely no trace of version information in the original source and it is unlikely that the original author will ever release another version, just set the version string to `1.0` (like the `piewm` example above). Otherwise, ask the original author or use the date string the source file was released on (`d__yyyy.mm.dd__`, or `d__yyyymmdd__`) as the version. [TIP] ==== Use any letter. Here, `d` here stands for date, if the source is a Git repository, `g` followed by the commit date is commonly used, using `s` for snapshot is also common. ==== [[makefile-categories]] == Categorization [[makefile-categories-definition]] === `CATEGORIES` When a package is created, it is put under [.filename]#/usr/ports/packages/All# and links are made from one or more subdirectories of [.filename]#/usr/ports/packages#. The names of these subdirectories are specified by the variable `CATEGORIES`. It is intended to make life easier for the user when he is wading through the pile of packages on the FTP site or the CDROM. Please take a look at the crossref:makefiles[porting-categories,current list of categories] and pick the ones that are suitable for the port. This list also determines where in the ports tree the port is imported. If there is more than one category here, the port files must be put in the subdirectory with the name of the first category. See crossref:makefiles[choosing-categories,below] for more discussion about how to pick the right categories. [[porting-categories]] === Current List of Categories Here is the current list of port categories. Those marked with an asterisk (`*`) are _virtual_ categories-those that do not have a corresponding subdirectory in the ports tree. They are only used as secondary categories, and only for search purposes. [NOTE] ==== For non-virtual categories, there is a one-line description in `COMMENT` in that subdirectory's [.filename]#Makefile#. ==== [.informaltable] [cols="1,1,1", frame="none", options="header"] |=== | Category | Description | Notes |[.filename]#accessibility# |Ports to help disabled users. | |[.filename]#afterstep#`*` |Ports to support the http://www.afterstep.org/[AfterStep] window manager. | |[.filename]#arabic# |Arabic language support. | |[.filename]#archivers# |Archiving tools. | |[.filename]#astro# |Astronomical ports. | |[.filename]#audio# |Sound support. | |[.filename]#benchmarks# |Benchmarking utilities. | |[.filename]#biology# |Biology-related software. | |[.filename]#budgie#`*` |Software related to the Budgie desktop environment. | |[.filename]#cad# |Computer aided design tools. | |[.filename]#chinese# |Chinese language support. | |[.filename]#comms# |Communication software. |Mostly software to talk to the serial port. |[.filename]#converters# |Character code converters. | |[.filename]#databases# |Databases. | |[.filename]#deskutils# |Things that used to be on the desktop before computers were invented. | |[.filename]#devel# |Development utilities. |Do not put libraries here just because they are libraries. They should _not_ be in this category unless they truly do not belong anywhere else. |[.filename]#dns# |DNS-related software. | |[.filename]#docs#`*` |Meta-ports for FreeBSD documentation. | |[.filename]#editors# |General editors. |Specialized editors go in the section for those tools. For example, a mathematical-formula editor will go in [.filename]#math#, and have [.filename]#editors# as a second category. |[.filename]#education#`*` |Education-related software. |This includes applications, utilities, or games primarily or substantially designed to help the user learn a specific topic or study in general. It also includes course-writing applications, course-delivery applications, and classroom or school management applications |[.filename]#elisp#`*` |Emacs-lisp ports. | |[.filename]#emulators# |Emulators for other operating systems. |Terminal emulators do _not_ belong here. X-based ones go to [.filename]#x11# and text-based ones to either [.filename]#comms# or [.filename]#misc#, depending on the exact functionality. |[.filename]#enlightenment#`*` |Ports related to the Enlightenment window manager. | |[.filename]#filesystems# |File systems and related utilities. | |[.filename]#finance# |Monetary, financial and related applications. | |[.filename]#french# |French language support. | |[.filename]#ftp# |FTP client and server utilities. |If the port speaks both FTP and HTTP, put it in [.filename]#ftp# with a secondary category of [.filename]#www#. |[.filename]#games# |Games. | |[.filename]#geography#`*` |Geography-related software. | |[.filename]#german# |German language support. | |[.filename]#gnome#`*` |Ports from the https://www.gnome.org/[GNOME] Project. | |[.filename]#gnustep#`*` |Software related to the GNUstep desktop environment. | |[.filename]#graphics# |Graphics utilities. | |[.filename]#hamradio#`*` |Software for amateur radio. | |[.filename]#haskell#`*` |Software related to the Haskell language. | |[.filename]#hebrew# |Hebrew language support. | |[.filename]#hungarian# |Hungarian language support. | |[.filename]#irc# |Internet Relay Chat utilities. | |[.filename]#japanese# |Japanese language support. | |[.filename]#java# |Software related to the Java(TM) language. |The [.filename]#java# category must not be the only one for a port. Save for ports directly related to the Java language, porters are also encouraged not to use [.filename]#java# as the main category of a port. |[.filename]#kde#`*` |Ports from the https://www.kde.org/[KDE] Project (generic). | |[.filename]#kde-applications#`*` |Applications from the https://www.kde.org/[KDE] Project. | |[.filename]#kde-frameworks#`*` |Add-on libraries from the https://www.kde.org/[KDE] Project for programming with Qt. | |[.filename]#kde-plasma#`*` |Desktop from the https://www.kde.org/[KDE] Project. | |[.filename]#kld#`*` |Kernel loadable modules. | |[.filename]#korean# |Korean language support. | |[.filename]#lang# |Programming languages. | |[.filename]#linux#`*` |Linux applications and support utilities. | |[.filename]#lisp#`*` |Software related to the Lisp language. | |[.filename]#mail# |Mail software. | |[.filename]#mate#`*` |Ports related to the MATE desktop environment, a fork of GNOME 2. | |[.filename]#math# |Numerical computation software and other utilities for mathematics. | |[.filename]#mbone#`*` |MBone applications. | |[.filename]#misc# |Miscellaneous utilities |Things that do not belong anywhere else. If at all possible, try to find a better category for the port than `misc`, as ports tend to be overlooked in here. |[.filename]#multimedia# |Multimedia software. | |[.filename]#net# |Miscellaneous networking software. | |[.filename]#net-im# |Instant messaging software. | |[.filename]#net-mgmt# |Networking management software. | |[.filename]#net-p2p# |Peer to peer network applications. | |[.filename]#net-vpn#`*` |Virtual Private Network applications. | |[.filename]#news# |USENET news software. | |[.filename]#parallel#`*` |Applications dealing with parallelism in computing. | |[.filename]#pear#`*` |Ports related to the Pear PHP framework. | |[.filename]#perl5#`*` |Ports that require Perl version 5 to run. | |[.filename]#plan9#`*` |Various programs from https://9p.io/wiki/plan9/Download/index.html[Plan9]. | |[.filename]#polish# |Polish language support. | |[.filename]#ports-mgmt# |Ports for managing, installing and developing FreeBSD ports and packages. | |[.filename]#portuguese# |Portuguese language support. | |[.filename]#print# |Printing software. |Desktop publishing tools (previewers, etc.) belong here too. |[.filename]#python#`*` |Software related to the https://www.python.org/[Python] language. | |[.filename]#ruby#`*` |Software related to the https://www.ruby-lang.org/[Ruby] language. | |[.filename]#rubygems#`*` |Ports of https://www.rubygems.org/[RubyGems] packages. | |[.filename]#russian# |Russian language support. | |[.filename]#scheme#`*` |Software related to the Scheme language. | |[.filename]#science# |Scientific ports that do not fit into other categories such as [.filename]#astro#, [.filename]#biology# and [.filename]#math#. | |[.filename]#security# |Security utilities. | |[.filename]#shells# |Command line shells. | |[.filename]#spanish#`*` |Spanish language support. | |[.filename]#sysutils# |System utilities. | |[.filename]#tcl#`*` |Ports that use Tcl to run. | |[.filename]#textproc# |Text processing utilities. |It does not include desktop publishing tools, which go to [.filename]#print#. |[.filename]#tk#`*` |Ports that use Tk to run. | |[.filename]#ukrainian# |Ukrainian language support. | |[.filename]#vietnamese# |Vietnamese language support. | |[.filename]#wayland#`*` |Ports to support the Wayland display server. | |[.filename]#windowmaker#`*` |Ports to support the Window Maker window manager. | |[.filename]#www# |Software related to the World Wide Web. |HTML language support belongs here too. |[.filename]#x11# |The X Window System and friends. |This category is only for software that directly supports the window system. Do not put regular X applications here. Most of them go into other [.filename]#x11-*# categories (see below). |[.filename]#x11-clocks# |X11 clocks. | |[.filename]#x11-drivers# |X11 drivers. | |[.filename]#x11-fm# |X11 file managers. | |[.filename]#x11-fonts# |X11 fonts and font utilities. | |[.filename]#x11-servers# |X11 servers. | |[.filename]#x11-themes# |X11 themes. | |[.filename]#x11-toolkits# |X11 toolkits. | |[.filename]#x11-wm# |X11 window managers. | |[.filename]#xfce#`*` |Ports related to the https://www.xfce.org/[Xfce] desktop environment. | |[.filename]#zope#`*` |https://www.zope.org/[Zope] support. | |=== [[choosing-categories]] === Choosing the Right Category As many of the categories overlap, choosing which of the categories will be the primary category of the port can be tedious. There are several rules that govern this issue. Here is the list of priorities, in decreasing order of precedence: * The first category must be a physical category (see crossref:makefiles[porting-categories,above]). This is necessary to make the packaging work. Virtual categories and physical categories may be intermixed after that. * Language specific categories always come first. For example, if the port installs Japanese X11 fonts, then the `CATEGORIES` line would read [.filename]#japanese x11-fonts#. * Specific categories are listed before less-specific ones. For instance, an HTML editor is listed as [.filename]#www editors#, not the other way around. Also, do not list [.filename]#net# when the port belongs to any of [.filename]#irc#, [.filename]#mail#, [.filename]#news#, [.filename]#security#, or [.filename]#www#, as [.filename]#net# is included implicitly. * [.filename]#x11# is used as a secondary category only when the primary category is a natural language. In particular, do not put [.filename]#x11# in the category line for X applications. * Emacs modes are placed in the same ports category as the application supported by the mode, not in [.filename]#editors#. For example, an Emacs mode to edit source files of some programming language goes into [.filename]#lang#. * Ports installing loadable kernel modules also have the virtual category [.filename]#kld# in their `CATEGORIES` line. This is one of the things handled automatically by adding `USES=kmod`. * [.filename]#misc# does not appear with any other non-virtual category. If there is `misc` with something else in `CATEGORIES`, that means `misc` can safely be deleted and the port placed only in the other subdirectory. * If the port truly does not belong anywhere else, put it in [.filename]#misc#. If the category is not clearly defined, please put a comment to that effect in the https://bugs.freebsd.org/submit/[port submission] in the bug database so we can discuss it before we import it. As a committer, send a note to the {freebsd-ports} so we can discuss it first. Too often, new ports are imported to the wrong category only to be moved right away. [[proposing-categories]] === Proposing a New Category As the Ports Collection has grown over time, various new categories have been introduced. New categories can either be _virtual_ categories-those that do not have a corresponding subdirectory in the ports tree- or _physical_ categories-those that do. This section discusses the issues involved in creating a new physical category. Read it thoroughly before proposing a new one. Our existing practice has been to avoid creating a new physical category unless either a large number of ports would logically belong to it, or the ports that would belong to it are a logically distinct group that is of limited general interest (for instance, categories related to spoken human languages), or preferably both. The rationale for this is that such a change creates a extref:{committers-guide}[fair amount of work, ports] for both the committers and also for all users who track changes to the Ports Collection. In addition, proposed category changes just naturally seem to attract controversy. (Perhaps this is because there is no clear consensus on when a category is "too big", nor whether categories should lend themselves to browsing (and thus what number of categories would be an ideal number), and so forth.) Here is the procedure: [.procedure] . Propose the new category on {freebsd-ports}. Include a detailed rationale for the new category, including why the existing categories are not sufficient, and the list of existing ports proposed to move. (If there are new ports pending in Bugzilla that would fit this category, list them too.) Indicating that the updater is also the maintainer or submitter may be helpful to the case. . Participate in the discussion. . If it seems that there is support for the idea, file a PR which includes both the rationale and the list of existing ports that need to be moved. Ideally, this PR would also include these patches: ** [.filename]##Makefile##s for the new ports once they are repocopied ** [.filename]#Makefile# for the new category ** [.filename]#Makefile# for the old ports' categories ** [.filename]##Makefile##s for ports that depend on the old ports ** (for extra credit, include the other files that have to change, as per the procedure in the Committer's Guide.) . Since it affects the ports infrastructure and involves moving and patching many ports but also possibly running regression tests on the build cluster, assign the PR to the {portmgr}. . If that PR is approved, a committer will need to follow the rest of the procedure that is extref:{committers-guide}[outlined in the Committer's Guide, ports]. Proposing a new virtual category is similar to the above but much less involved, since no ports will actually have to move. In this case, the only patches to include in the PR would be those to add the new category to `CATEGORIES` of the affected ports. [[proposing-reorg]] === Proposing Reorganizing All the Categories Occasionally someone proposes reorganizing the categories with either a 2-level structure, or some other kind of keyword structure. To date, nothing has come of any of these proposals because, while they are very easy to make, the effort involved to retrofit the entire existing ports collection with any kind of reorganization is daunting to say the very least. Please read the history of these proposals in the mailing list archives before posting this idea. Furthermore, be prepared to be challenged to offer a working prototype. [[makefile-distfiles]] == The Distribution Files The second part of the [.filename]#Makefile# describes the files that must be downloaded to build the port, and where they can be downloaded. [[makefile-distname]] === `DISTNAME` `DISTNAME` is the name of the port as called by the authors of the software. `DISTNAME` defaults to `${PORTNAME}-${DISTVERSIONPREFIX}${DISTVERSION}${DISTVERSIONSUFFIX}`, and if not set, `DISTVERSION` defaults to `${PORTVERSION}` so override `DISTNAME` only if necessary. `DISTNAME` is only used in two places. First, the distribution file list (`DISTFILES`) defaults to `${DISTNAME}${EXTRACT_SUFX}`. Second, the distribution file is expected to extract into a subdirectory named `WRKSRC`, which defaults to [.filename]#work/${DISTNAME}#. Some vendor's distribution names which do not fit into the `${PORTNAME}-${PORTVERSION}`-scheme can be handled automatically by setting `DISTVERSIONPREFIX`, `DISTVERSION`, and `DISTVERSIONSUFFIX`. `PORTVERSION` will be derived from `DISTVERSION` automatically. [IMPORTANT] ==== Only one of `PORTVERSION` and `DISTVERSION` can be set at a time. If `DISTVERSION` does not derive a correct `PORTVERSION`, do not use `DISTVERSION`. ==== If the upstream version scheme can be derived into a ports-compatible version scheme, set some variable to the upstream version, _do not_ use `DISTVERSION` as the variable name. Set `PORTVERSION` to the computed version based on the created variable and set `DISTNAME` accordingly. If the upstream version scheme cannot easily be coerced into a ports-compatible value, set `PORTVERSION` to a sensible value, and set `DISTNAME` with `PORTNAME` with the verbatim upstream version. [[makefile-distname-ex1]] .Deriving `PORTVERSION` Manually [example] ==== BIND9 uses a version scheme that is not compatible with the ports versions (it has `-` in its versions) and cannot be derived using `DISTVERSION` because after the 9.9.9 release, it will release a "patchlevels" in the form of `9.9.9-P1`. DISTVERSION would translate that into `9.9.9.p1`, which, in the ports versioning scheme means 9.9.9 pre-release 1, which is before 9.9.9 and not after. So `PORTVERSION` is manually derived from an `ISCVERSION` variable to output `9.9.9p1`. The order into which the ports framework, and pkg, will sort versions is checked using the `-t` argument of man:pkg-version[8]: [source,shell] .... % pkg version -t 9.9.9 9.9.9.p1 > <.> % pkg version -t 9.9.9 9.9.9p1 < <.> .... <.> The `>` sign means that the first argument passed to `-t` is greater than the second argument. `9.9.9` is after `9.9.9.p1`. <.> The `<` sign means that the first argument passed to `-t` is less than the second argument. `9.9.9` is before `9.9.9p1`. In the port [.filename]#Makefile#, for example package:dns/bind99[], it is achieved by: [.programlisting] .... PORTNAME= bind PORTVERSION= ${ISCVERSION:S/-P/P/:S/b/.b/:S/a/.a/:S/rc/.rc/} CATEGORIES= dns net MASTER_SITES= ISC/bind9/${ISCVERSION} PKGNAMESUFFIX= 99 DISTNAME= ${PORTNAME}-${ISCVERSION} MAINTAINER= mat@FreeBSD.org COMMENT= BIND DNS suite with updated DNSSEC and DNS64 WWW= https://www.isc.org/bind/ LICENSE= ISCL # ISC releases things like 9.8.0-P1 or 9.8.1rc1, which our versioning does not like ISCVERSION= 9.9.9-P6 .... Define upstream version in `ISCVERSION`, with a comment saying _why_ it is needed. Use `ISCVERSION` to get a ports-compatible `PORTVERSION`. Use `ISCVERSION` directly to get the correct URL for fetching the distribution file. Use `ISCVERSION` directly to name the distribution file. ==== [[makefile-distname-ex2]] .Derive `DISTNAME` from `PORTVERSION` [example] ==== From time to time, the distribution file name has little or no relation to the version of the software. In package:comms/kermit[], only the last element of the version is present in the distribution file: [.programlisting] .... PORTNAME= kermit PORTVERSION= 9.0.304 CATEGORIES= comms ftp net MASTER_SITES= ftp://ftp.kermitproject.org/kermit/test/tar/ DISTNAME= cku${PORTVERSION:E}-dev20 .... The `:E` man:make[1] modifier returns the suffix of the variable, in this case, `304`. The distribution file is correctly generated as `cku304-dev20.tar.gz`. ==== [[makefile-distname-ex3]] .Exotic Case 1 [example] ==== Sometimes, there is no relation between the software name, its version, and the distribution file it is distributed in. From package:audio/libworkman[]: [.programlisting] .... PORTNAME= libworkman PORTVERSION= 1.4 CATEGORIES= audio MASTER_SITES= LOCAL/jim DISTNAME= ${PORTNAME}-1999-06-20 .... ==== [[makefile-distname-ex4]] .Exotic Case 2 [example] ==== In package:comms/librs232[], the distribution file is not versioned, so using crossref:makefiles[makefile-dist_subdir,`DIST_SUBDIR`] is needed: [.programlisting] .... PORTNAME= librs232 PORTVERSION= 20160710 CATEGORIES= comms MASTER_SITES= http://www.teuniz.net/RS-232/ DISTNAME= RS-232 DIST_SUBDIR= ${PORTNAME}-${PORTVERSION} .... ==== [NOTE] ==== `PKGNAMEPREFIX` and `PKGNAMESUFFIX` do not affect `DISTNAME`. Also note that if `WRKSRC` is equal to [.filename]#${WRKDIR}/${DISTNAME}# while the original source archive is named something other than `${PORTNAME}-${PORTVERSION}${EXTRACT_SUFX}`, leave `DISTNAME` alone- defining only `DISTFILES` is easier than both `DISTNAME` and `WRKSRC` (and possibly `EXTRACT_SUFX`). ==== [[makefile-master_sites]] === `MASTER_SITES` Record the directory part of the FTP/HTTP-URL pointing at the original tarball in `MASTER_SITES`. Do not forget the trailing slash ([.filename]#/#)! The `make` macros will try to use this specification for grabbing the distribution file with `FETCH` if they cannot find it already on the system. It is recommended that multiple sites are included on this list, preferably from different continents. This will safeguard against wide-area network problems. [IMPORTANT] ==== `MASTER_SITES` must not be blank. It must point to the actual site hosting the distribution files. It cannot point to web archives, or the FreeBSD distribution files cache sites. The only exception to this rule is ports that do not have any distribution files. For example, meta-ports do not have any distribution files, so `MASTER_SITES` does not need to be set. ==== [[makefile-master_sites-shorthand]] ==== Using `MASTER_SITE_*` Variables Shortcut abbreviations are available for popular archives like SourceForge (`SOURCEFORGE`), GNU (`GNU`), or Perl CPAN (`PERL_CPAN`). `MASTER_SITES` can use them directly: [.programlisting] .... MASTER_SITES= GNU/make .... The older expanded format still works, but all ports have been converted to the compact format. The expanded format looks like this: [.programlisting] .... MASTER_SITES= ${MASTER_SITE_GNU} MASTER_SITE_SUBDIR= make .... These values and variables are defined in https://cgit.freebsd.org/ports/tree/Mk/bsd.sites.mk[Mk/bsd.sites.mk]. New entries are added often, so make sure to check the latest version of this file before submitting a port. [TIP] ==== For any `MASTER_SITE_FOO` variable, the shorthand `_FOO_` can be used. For example, use: [.programlisting] .... MASTER_SITES= FOO .... If `MASTER_SITE_SUBDIR` is needed, use this: [.programlisting] .... MASTER_SITES= FOO/bar .... ==== [NOTE] ==== Some `MASTER_SITE_*` names are quite long, and for ease of use, shortcuts have been defined: [[makefile-master_sites-shortcut]] .Shortcuts for `MASTER_SITE_*` Macros [cols="1,1", frame="none", options="header"] |=== | Macro | Shortcut |`PERL_CPAN` |`CPAN` |`GITHUB` |`GH` |`GITHUB_CLOUD` |`GHC` |`LIBREOFFICE_DEV` |`LODEV` |`NETLIB` |`NL` |`RUBYGEMS` |`RG` |`SOURCEFORGE` |`SF` |=== ==== [[makefile-master_sites-magic]] ==== Magic MASTER_SITES Macros Several "magic" macros exist for popular sites with a predictable directory structure. For these, just use the abbreviation and the system will choose a subdirectory automatically. For a port named `Stardict`, of version `1.2.3`, and hosted on SourceForge, adding this line: [.programlisting] .... MASTER_SITES= SF .... infers a subdirectory named `/project/stardict/stardict/1.2.3`. If the inferred directory is incorrect, it can be overridden: [.programlisting] .... MASTER_SITES= SF/stardict/WyabdcRealPeopleTTS/${PORTVERSION} .... This can also be written as [.programlisting] .... MASTER_SITES= SF MASTER_SITE_SUBDIR= stardict/WyabdcRealPeopleTTS/${PORTVERSION} .... [[makefile-master_sites-popular]] .Magic `MASTER_SITES` Macros [cols="1,1", frame="none", options="header"] |=== | Macro | Assumed subdirectory |`APACHE_COMMONS_BINARIES` |`${PORTNAME:S,commons-,,}` |`APACHE_COMMONS_SOURCE` |`${PORTNAME:S,commons-,,}` |`APACHE_JAKARTA` |`${PORTNAME:S,-,/,}/source` |`BERLIOS` |`${PORTNAME:tl}.berlios` |`PYPI` |`source/${DISTNAME:C/(.).\*/\1/}/${DISTNAME:C/(.*)-[0-9].*/\1/}` |`CPAN` |`${PORTNAME:C/-.*//}` |`DEBIAN` |`pool/main/${PORTNAME:C/^((lib)?.).*$/\1/}/${PORTNAME}` |`FARSIGHT` |`${PORTNAME}` |`FESTIVAL` |`${PORTREVISION}` |`GCC` |`releases/${DISTNAME}` |`GENTOO` |`distfiles` |`GIMP` |`${PORTNAME}/${PORTVERSION:R}/` |`GH` |`${GH_ACCOUNT}/${GH_PROJECT}/tar.gz/${GH_TAGNAME}?dummy=/` |`GHC` |`${GH_ACCOUNT}/${GH_PROJECT}/` |`GNOME` |`sources/${PORTNAME}/${PORTVERSION:C/^([0-9]+\.[0-9]+).*/\1/}` |`GNU` |`${PORTNAME}` |`GNUPG` |`${PORTNAME}` |`GNU_ALPHA` |`${PORTNAME}` |`HORDE` |`${PORTNAME}` |`LODEV` |`${PORTNAME}` |`MATE` |`${PORTVERSION:C/^([0-9]+\.[0-9]+).*/\1/}` |`MOZDEV` |`${PORTNAME:tl}` |`NL` |`${PORTNAME}` |`QT` |`archive/qt/${PORTVERSION:R}` |`SAMBA` |`${PORTNAME}` |`SAVANNAH` |`${PORTNAME:tl}` |`SF` |`${PORTNAME:tl}/${PORTNAME:tl}/${PORTVERSION}` |=== [[makefile-master_sites-github]] === `USE_GITHUB` If the distribution file comes from a specific commit or tag on https://github.com/[GitHub] for which there is no officially released file, there is an easy way to set the right `DISTNAME` and `MASTER_SITES` automatically. [WARNING] ==== As of 2023-02-21 link:https://github.blog/2023-02-21-update-on-the-future-stability-of-source-code-archives-and-hashes/[GitHub] have announced that source downloads will be stable for a year. Please switch to release assets and if not available ask upstream to generate ones. ==== These variables are available: [[makefile-master_sites-github-description]] .`USE_GITHUB` Description [cols="1,1,1", options="header"] |=== | Variable | Description | Default |`GH_ACCOUNT` |Account name of the GitHub user hosting the project |`${PORTNAME}` |`GH_PROJECT` |Name of the project on GitHub |`${PORTNAME}` |`GH_TAGNAME` |Name of the tag to download (2.0.1, hash, ...) Using the name of a branch here is incorrect. It is also possible to use the hash of a commit id to do a snapshot. |`${DISTVERSIONPREFIX}${DISTVERSION}${DISTVERSIONSUFFIX}` |`GH_SUBDIR` |When the software needs an additional distribution file to be extracted within `${WRKSRC}`, this variable can be used. See the examples in crossref:makefiles[makefile-master_sites-github-multiple, Fetching Multiple Files from GitHub] for more information. |(none) |`GH_TUPLE` |`GH_TUPLE` allows putting `GH_ACCOUNT`, `GH_PROJECT`, `GH_TAGNAME`, and `GH_SUBDIR` into a single variable. The format is _account_`:`_project_`:`_tagname_`:`_group_`/`_subdir_. The `/`_subdir_ part is optional. It is helpful when there is more than one GitHub project from which to fetch. | |=== [IMPORTANT] ==== Do not use `GH_TUPLE` for the default distribution file, as it has no default. ==== [[makefile-master_sites-github-ex1]] .Simple Use of `USE_GITHUB` [example] ==== While trying to make a port for version `1.2.7` of pkg from the FreeBSD user on github, at https://github.com/freebsd/pkg/[], The [.filename]#Makefile# would end up looking like this (slightly stripped for the example): [.programlisting] .... PORTNAME= pkg DISTVERSION= 1.2.7 USE_GITHUB= yes GH_ACCOUNT= freebsd .... It will automatically have `MASTER_SITES` set to `GH` and `WRKSRC` to `${WRKDIR}/pkg-1.2.7`. ==== [[makefile-master_sites-github-ex2]] .More Complete Use of `USE_GITHUB` [example] ==== While trying to make a port for the bleeding edge version of pkg from the FreeBSD user on github, at https://github.com/freebsd/pkg/[], the [.filename]#Makefile# ends up looking like this (slightly stripped for the example): [.programlisting] .... PORTNAME= pkg-devel DISTVERSION= 2.7.4.20260626 USE_GITHUB= yes GH_ACCOUNT= freebsd GH_PROJECT= pkg GH_TAGNAME= 2678d2b6a8ca3cf80cb4dbc8da557a2998e1b5c0 .... It will automatically have `MASTER_SITES` set to `GH` and `WRKSRC` to `${WRKDIR}/pkg-6dbb17b`. [TIP] -**** +==== `20260626` is the date of the commit referenced in `GH_TAGNAME`, not the date the [.filename]#Makefile# is edited, or the date the commit is made. -**** +==== ==== [[makefile-master_sites-github-ex3]] .Use of `USE_GITHUB` with `DISTVERSIONPREFIX` [example] ==== From time to time, `GH_TAGNAME` is a slight variation from `DISTVERSION`. For example, if the version is `1.0.2`, the tag is `v1.0.2`. In those cases, it is possible to use `DISTVERSIONPREFIX` or `DISTVERSIONSUFFIX`: [.programlisting] .... PORTNAME= foo DISTVERSIONPREFIX= v DISTVERSION= 1.0.2 USE_GITHUB= yes .... It will automatically set `GH_TAGNAME` to `v1.0.2`, while `WRKSRC` will be kept to `${WRKDIR}/foo-1.0.2`. ==== [[makefile-master_sites-github-ex4]] .Using `USE_GITHUB` When Upstream Does Not Use Versions [example] ==== If there never was a version upstream, do not invent one like `0.1` or `1.0`. Create the port with a `DISTVERSION` of `g__YYYYMMDD__`, where `g` is for Git, and `_YYYYMMDD_` represents the date the commit referenced in `GH_TAGNAME`. [.programlisting] .... PORTNAME= bar DISTVERSION= g20260626 USE_GITHUB= yes GH_TAGNAME= c472d66b70dd603bf9e67607f0869639276796ce .... This creates a versioning scheme that increases over time, and that is still before version `0`. See crossref:makefiles[makefile-versions-ex-pkg-version, this secion on how to compare versions] using man:pkg-version[8]: [source,shell] .... % pkg version -t g20260626 0 < .... Which means using `PORTEPOCH` will not be needed in case upstream decides to cut versions in the future. ==== [[makefile-master_sites-github-ex5]] .Using `USE_GITHUB` to Access a Commit Between Two Versions [example] ==== If the current version of the software uses a Git tag, and the port needs to be updated to a newer, intermediate version, without a tag, use man:git-describe[1] to find out the version to use: [source,shell] .... % git describe --tags f0038b1 v0.7.3-14-gf0038b1 .... `v0.7.3-14-gf0038b1` can be split into three parts: `v0.7.3`:: This is the last Git tag that appears in the commit history before the requested commit. `-14`:: This means that the requested commit, `f0038b1`, is the 14th commit after the `v0.7.3` tag. `-gf0038b1`:: The `-g` means "Git", and the `f0038b1` is the commit hash that this reference points to. [.programlisting] .... PORTNAME= bar DISTVERSIONPREFIX= v DISTVERSION= 0.7.3-14 DISTVERSIONSUFFIX= -gf0038b1 USE_GITHUB= yes .... This creates a versioning scheme that increases over time (well, over commits), and does not conflict with the creation of a `0.7.4` version. See crossref:makefiles[makefile-versions-ex-pkg-version, this section for how to compare versions] using man:pkg-version[8]: [source,shell] .... % pkg version -t 0.7.3 0.7.3.14 < % pkg version -t 0.7.3.14 0.7.4 < .... [NOTE] -**** +===== If the requested commit is the same as a tag, a shorter description is shown by default. The longer version is equivalent: [source,shell] .... % git describe --tags c66c71d v0.7.3 % git describe --tags --long c66c71d v0.7.3-0-gc66c71d .... -**** +===== ==== [[makefile-master_sites-github-multiple]] ==== Fetching Multiple Files from GitHub The `USE_GITHUB` framework also supports fetching multiple distribution files from different places in GitHub. It works in a way very similar to crossref:makefiles[porting-master-sites-n, Multiple Distribution or Patches Files from Multiple Locations]. Multiple values are added to `GH_ACCOUNT`, `GH_PROJECT`, and `GH_TAGNAME`. Each different value is assigned a group. The main value can either have no group, or the `:DEFAULT` group. A value can be omitted if it is the same as the default as listed in crossref:makefiles[makefile-master_sites-github-description,`USE_GITHUB` Description]. `GH_TUPLE` can also be used when there are a lot of distribution files. It helps keep the account, project, tagname, and group information at the same place. For each group, a `${WRKSRC_group}` helper variable is created, containing the directory into which the file has been extracted. The `${WRKSRC_group}` variables can be used to move directories around during `post-extract`, or add to `CONFIGURE_ARGS`, or whatever is needed so that the software builds correctly. [CAUTION] ==== The `:__group__` part _must_ be used for _only one_ distribution file. It is used as a unique key and using it more than once will overwrite the previous values. ==== [NOTE] ==== As this is only syntactic sugar above `DISTFILES` and `MASTER_SITES`, the group names must adhere to the restrictions on group names outlined in crossref:makefiles[porting-master-sites-n, Multiple Distribution or Patches Files from Multiple Locations] ==== When fetching multiple files from GitHub, sometimes the default distribution file is not fetched from GitHub. To disable fetching the default distribution, set: [.programlisting] .... USE_GITHUB= nodefault .... [IMPORTANT] ==== When using `USE_GITHUB=nodefault`, the [.filename]#Makefile# must set `DISTFILES` in its crossref:porting-order[porting-order-portname,top block]. The definition should be: [.programlisting] .... DISTFILES= ${DISTNAME}${EXTRACT_SUFX} .... ==== [[makefile-master_sites-github-multi]] .Use of `USE_GITHUB` with Multiple Distribution Files [example] ==== From time to time, there is a need to fetch more than one distribution file. For example, when the upstream git repository uses submodules. This can be done easily using groups in the `GH_*` variables: [.programlisting] .... PORTNAME= foo DISTVERSION= 1.0.2 USE_GITHUB= yes GH_ACCOUNT= bar:icons,contrib GH_PROJECT= foo-icons:icons foo-contrib:contrib GH_TAGNAME= 1.0:icons fa579bc:contrib GH_SUBDIR= ext/icons:icons CONFIGURE_ARGS= --with-contrib=${WRKSRC_contrib} .... This will fetch three distribution files from github. The default one comes from [.filename]#foo/foo# and is version `1.0.2`. The second one, with the `icons` group, comes from [.filename]#bar/foo-icons# and is in version `1.0`. The third one comes from [.filename]#bar/foo-contrib# and uses the Git commit `fa579bc`. The distribution files are named [.filename]#foo-foo-1.0.2_GH0.tar.gz#, [.filename]#bar-foo-icons-1.0_GH0.tar.gz#, and [.filename]#bar-foo-contrib-fa579bc_GH0.tar.gz#. All the distribution files are extracted in `${WRKDIR}` in their respective subdirectories. The default file is still extracted in `${WRKSRC}`, in this case, [.filename]#${WRKDIR}/foo-1.0.2#. Each additional distribution file is extracted in `${WRKSRC_group}`. Here, for the `icons` group, it is called `${WRKSRC_icons}` and it contains [.filename]#${WRKDIR}/foo-icons-1.0#. The file with the `contrib` group is called `${WRKSRC_contrib}` and contains `${WRKDIR}/foo-contrib-fa579bc`. The software's build system expects to find the icons in a [.filename]#ext/icons# subdirectory in its sources, so `GH_SUBDIR` is used. `GH_SUBDIR` makes sure that [.filename]#ext# exists, but that [.filename]#ext/icons# does not already exist. Then it does this: [.programlisting] .... post-extract: @${MV} ${WRKSRC_icons} ${WRKSRC}/ext/icons .... ==== [[makefile-master_sites-github-multi2]] .Use of `USE_GITHUB` with Multiple Distribution Files Using `GH_TUPLE` [example] ==== This is functionally equivalent to crossref:makefiles[makefile-master_sites-github-multi,Use of `USE_GITHUB` with Multiple Distribution Files], but using `GH_TUPLE`: [.programlisting] .... PORTNAME= foo DISTVERSION= 1.0.2 USE_GITHUB= yes GH_TUPLE= bar:foo-icons:1.0:icons/ext/icons \ bar:foo-contrib:fa579bc:contrib CONFIGURE_ARGS= --with-contrib=${WRKSRC_contrib} .... Grouping was used in the previous example with `bar:icons,contrib`. Some redundant information is present with `GH_TUPLE` because grouping is not possible. ==== [[makefile-master_sites-github-submodules]] .How to Use `USE_GITHUB` with Git Submodules? [example] ==== Ports with GitHub as an upstream repository sometimes use submodules. See man:git-submodule[1] for more information. The problem with submodules is that each is a separate repository. As such, they each must be fetched separately. Using package:finance/moneymanagerex[] as an example, its GitHub repository is https://github.com/moneymanagerex/moneymanagerex/[]. It has a https://github.com/moneymanagerex/moneymanagerex/blob/master/.gitmodules[.gitmodules] file at the root. This file describes all the submodules used in this repository, and lists additional repositories needed. This file will tell what additional repositories are needed: [.programlisting] .... [submodule "lib/wxsqlite3"] path = lib/wxsqlite3 url = https://github.com/utelle/wxsqlite3.git [submodule "3rd/mongoose"] path = 3rd/mongoose url = https://github.com/cesanta/mongoose.git [submodule "3rd/LuaGlue"] path = 3rd/LuaGlue url = https://github.com/moneymanagerex/LuaGlue.git [submodule "3rd/cgitemplate"] path = 3rd/cgitemplate url = https://github.com/moneymanagerex/html-template.git [...] .... The only information missing from that file is the commit hash or tag to use as a version. This information is found after cloning the repository: [source,shell] .... % git clone --recurse-submodules https://github.com/moneymanagerex/moneymanagerex.git Cloning into 'moneymanagerex'... remote: Counting objects: 32387, done. [...] Submodule '3rd/LuaGlue' (https://github.com/moneymanagerex/LuaGlue.git) registered for path '3rd/LuaGlue' Submodule '3rd/cgitemplate' (https://github.com/moneymanagerex/html-template.git) registered for path '3rd/cgitemplate' Submodule '3rd/mongoose' (https://github.com/cesanta/mongoose.git) registered for path '3rd/mongoose' Submodule 'lib/wxsqlite3' (https://github.com/utelle/wxsqlite3.git) registered for path 'lib/wxsqlite3' [...] Cloning into '/home/mat/work/freebsd/ports/finance/moneymanagerex/moneymanagerex/3rd/LuaGlue'... Cloning into '/home/mat/work/freebsd/ports/finance/moneymanagerex/moneymanagerex/3rd/cgitemplate'... Cloning into '/home/mat/work/freebsd/ports/finance/moneymanagerex/moneymanagerex/3rd/mongoose'... Cloning into '/home/mat/work/freebsd/ports/finance/moneymanagerex/moneymanagerex/lib/wxsqlite3'... [...] Submodule path '3rd/LuaGlue': checked out 'c51d11a247ee4d1e9817dfa2a8da8d9e2f97ae3b' Submodule path '3rd/cgitemplate': checked out 'cd434eeeb35904ebcd3d718ba29c281a649b192c' Submodule path '3rd/mongoose': checked out '2140e5992ab9a3a9a34ce9a281abf57f00f95cda' Submodule path 'lib/wxsqlite3': checked out 'fb66eb230d8aed21dec273b38c7c054dcb7d6b51' [...] % cd moneymanagerex % git submodule status c51d11a247ee4d1e9817dfa2a8da8d9e2f97ae3b 3rd/LuaGlue (heads/master) cd434eeeb35904ebcd3d718ba29c281a649b192c 3rd/cgitemplate (cd434ee) 2140e5992ab9a3a9a34ce9a281abf57f00f95cda 3rd/mongoose (6.2-138-g2140e59) fb66eb230d8aed21dec273b38c7c054dcb7d6b51 lib/wxsqlite3 (v3.4.0) [...] .... It can also be found on GitHub. Each subdirectory that is a submodule is shown as `_directory @ hash_`, for example, `mongoose @ 2140e59`. [NOTE] -**** +===== While getting the information from GitHub seems more straightforward, the information found using `git submodule status` will provide more meaningful information. For example, here, ``lib/wxsqlite3``'s commit hash `fb66eb2` correspond to `v3.4.0`. Both can be used interchangeably, but when a tag is available, use it. -**** +===== Now that all the required information has been gathered, the [.filename]#Makefile# can be written (only GitHub-related lines are shown): [.programlisting] .... PORTNAME= moneymanagerex DISTVERSIONPREFIX= v DISTVERSION= 1.3.0 USE_GITHUB= yes GH_TUPLE= utelle:wxsqlite3:v3.4.0:wxsqlite3/lib/wxsqlite3 \ moneymanagerex:LuaGlue:c51d11a:lua_glue/3rd/LuaGlue \ moneymanagerex:html-template:cd434ee:html_template/3rd/cgitemplate \ cesanta:mongoose:2140e59:mongoose/3rd/mongoose \ [...] .... ==== [[makefile-master_sites-gitlab]] === `USE_GITLAB` Similar to GitHub, if the distribution file comes from https://gitlab.com/[gitlab.com] or is hosting the GitLab software, these variables are available for use and might need to be set. [[makefile-master_sites-gitlab-description]] .`USE_GITLAB` Description [cols="1,1,1", options="header"] |=== | Variable | Description | Default |`GL_SITE` |Site name hosting the GitLab project |https://gitlab.com/ |`GL_ACCOUNT` |Account name of the GitLab user hosting the project |`${PORTNAME}` |`GL_PROJECT` |Name of the project on GitLab |`${PORTNAME}` |`GL_COMMIT` |The commit hash to download. Must be the full 160 bit, 40 character hex sha1 hash. This is a required variable for GitLab. |`(none)` |`GL_SUBDIR` |When the software needs an additional distribution file to be extracted within `${WRKSRC}`, this variable can be used. See the examples in crossref:makefiles[makefile-master_sites-gitlab-multiple, Fetching Multiple Files from GitLab] for more information. |(none) |`GL_TUPLE` |`GL_TUPLE` allows putting `GL_SITE`, `GL_ACCOUNT`, `GL_PROJECT`, `GL_COMMIT`, and `GL_SUBDIR` into a single variable. The format is _site_`:`_account_`:`_project_`:`_commit_`:`_group_`/`_subdir_. The _site_`:` and `/`_subdir_ part is optional. It is helpful when there are more than one GitLab project from which to fetch. | |=== [[makefile-master_sites-gitlab-ex1]] .Simple Use of `USE_GITLAB` [example] ==== While trying to make a port for version `1.14` of libsignon-glib from the accounts-sso user on gitlab.com, at https://gitlab.com/accounts-sso/libsignon-glib/[], The [.filename]#Makefile# would end up looking like this for fetching the distribution files: [.programlisting] .... PORTNAME= libsignon-glib DISTVERSION= 1.14 USE_GITLAB= yes GL_ACCOUNT= accounts-sso GL_COMMIT= e90302e342bfd27bc8c9132ab9d0ea3d8723fd03 .... It will automatically have `MASTER_SITES` set to https://gitlab.com/[gitlab.com] and `WRKSRC` to `${WRKDIR}/libsignon-glib-e90302e342bfd27bc8c9132ab9d0ea3d8723fd03-e90302e342bfd27bc8c9132ab9d0ea3d8723fd03`. ==== [[makefile-master_sites-gitlab-ex2]] .More Complete Use of `USE_GITLAB` [example] ==== A more complete use of the above if port had no versioning and foobar from the foo user on project bar on a self hosted GitLab site `https://gitlab.example.com/`, the [.filename]#Makefile# ends up looking like this for fetching distribution files: [.programlisting] .... PORTNAME= foobar DISTVERSION= g20170906 USE_GITLAB= yes GL_SITE= https://gitlab.example.com GL_ACCOUNT= foo GL_PROJECT= bar GL_COMMIT= 9c1669ce60c3f4f5eb43df874d7314483fb3f8a6 .... It will have `MASTER_SITES` set to `"https://gitlab.example.com"` and `WRKSRC` to `${WRKDIR}/bar-9c1669ce60c3f4f5eb43df874d7314483fb3f8a6-9c1669ce60c3f4f5eb43df874d7314483fb3f8a6`. [TIP] ====== `20170906` is the date of the commit referenced in `GL_COMMIT`, not the date the [.filename]#Makefile# is edited, or the date the commit to the FreeBSD ports tree is made. ====== [NOTE] ====== ``GL_SITE``'s protocol, port and webroot can all be modified in the same variable. ====== ==== [[makefile-master_sites-gitlab-multiple]] ==== Fetching Multiple Files from GitLab The `USE_GITLAB` framework also supports fetching multiple distribution files from different places from GitLab and GitLab hosted sites. It works in a way very similar to crossref:makefiles[porting-master-sites-n, Multiple Distribution or Patches Files from Multiple Locations] and crossref:makefiles[makefile-master_sites-github-multiple, Fetching Multiple Files from GitHub]. Multiple values are added to `GL_SITE`, `GL_ACCOUNT`, `GL_PROJECT` and `GL_COMMIT`. Each different value is assigned a group. crossref:makefiles[makefile-master_sites-gitlab-description,`USE_GITLAB` Description]. `GL_TUPLE` can also be used when there are a lot of distribution files. It helps keep the site, account, project, commit, and group information at the same place. For each group, a `${WRKSRC_group}` helper variable is created, containing the directory into which the file has been extracted. The `${WRKSRC_group}` variables can be used to move directories around during `post-extract`, or add to `CONFIGURE_ARGS`, or whatever is needed so that the software builds correctly. [CAUTION] ==== The `:__group__` part _must_ be used for _only one_ distribution file. It is used as a unique key and using it more than once will overwrite the previous values. ==== [NOTE] ==== As this is only syntactic sugar above `DISTFILES` and `MASTER_SITES`, the group names must adhere to the restrictions on group names outlined in crossref:makefiles[porting-master-sites-n, Multiple Distribution or Patches Files from Multiple Locations] ==== When fetching multiple files using GitLab, sometimes the default distribution file is not fetched from a GitLab site. To disable fetching the default distribution, set: [.programlisting] .... USE_GITLAB= nodefault .... [IMPORTANT] ==== When using `USE_GITLAB=nodefault`, the [.filename]#Makefile# must set `DISTFILES` in its crossref:makefiles[porting-order-portname,top block]. The definition should be: [.programlisting] .... DISTFILES= ${DISTNAME}${EXTRACT_SUFX} .... ==== [[makefile-master_sites-gitlab-multi]] .Use of `USE_GITLAB` with Multiple Distribution Files [example] ==== From time to time, there is a need to fetch more than one distribution file. For example, when the upstream git repository uses submodules. This can be done easily using groups in the `GL_*` variables: [.programlisting] .... PORTNAME= foo DISTVERSION= 1.0.2 USE_GITLAB= yes GL_SITE= https://gitlab.example.com:9434/gitlab:icons GL_ACCOUNT= bar:icons,contrib GL_PROJECT= foo-icons:icons foo-contrib:contrib GL_COMMIT= c189207a55da45305c884fe2b50e086fcad4724b ae7368cab1ca7ca754b38d49da064df87968ffe4:icons 9e4dd76ad9b38f33fdb417a4c01935958d5acd2a:contrib GL_SUBDIR= ext/icons:icons CONFIGURE_ARGS= --with-contrib=${WRKSRC_contrib} .... This will fetch two distribution files from gitlab.com and one from `gitlab.example.com` hosting GitLab. The default one comes from [.filename]#https://gitlab.com/foo/foo# and commit is `c189207a55da45305c884fe2b50e086fcad4724b`. The second one, with the `icons` group, comes from [.filename]#https://gitlab.example.com:9434/gitlab/bar/foo-icons# and commit is `ae7368cab1ca7ca754b38d49da064df87968ffe4`. The third one comes from [.filename]#https://gitlab.com/bar/foo-contrib# and is commit `9e4dd76ad9b38f33fdb417a4c01935958d5acd2a`. The distribution files are named [.filename]#foo-foo-c189207a55da45305c884fe2b50e086fcad4724b_GL0.tar.gz#, [.filename]#bar-foo-icons-ae7368cab1ca7ca754b38d49da064df87968ffe4_GL0.tar.gz#, and [.filename]#bar-foo-contrib-9e4dd76ad9b38f33fdb417a4c01935958d5acd2a_GL0.tar.gz#. All the distribution files are extracted in `${WRKDIR}` in their respective subdirectories. The default file is still extracted in `${WRKSRC}`, in this case, [.filename]#${WRKDIR}/foo-c189207a55da45305c884fe2b50e086fcad4724b-c189207a55da45305c884fe2b50e086fcad4724b#. Each additional distribution file is extracted in `${WRKSRC_group}`. Here, for the `icons` group, it is called `${WRKSRC_icons}` and it contains [.filename]#${WRKDIR}/foo-icons-ae7368cab1ca7ca754b38d49da064df87968ffe4-ae7368cab1ca7ca754b38d49da064df87968ffe4#. The file with the `contrib` group is called `${WRKSRC_contrib}` and contains `${WRKDIR}/foo-contrib-9e4dd76ad9b38f33fdb417a4c01935958d5acd2a-9e4dd76ad9b38f33fdb417a4c01935958d5acd2a`. The software's build system expects to find the icons in a [.filename]#ext/icons# subdirectory in its sources, so `GL_SUBDIR` is used. `GL_SUBDIR` makes sure that [.filename]#ext# exists, but that [.filename]#ext/icons# does not already exist. Then it does this: [.programlisting] .... post-extract: @${MV} ${WRKSRC_icons} ${WRKSRC}/ext/icons .... ==== [[makefile-master_sites-gitlab-multi2]] .Use of `USE_GITLAB` with Multiple Distribution Files Using `GL_TUPLE` [example] ==== This is functionally equivalent to crossref:makefiles[makefile-master_sites-gitlab-multi,Use of `USE_GITLAB` with Multiple Distribution Files], but using `GL_TUPLE`: [.programlisting] .... PORTNAME= foo DISTVERSION= 1.0.2 USE_GITLAB= yes GL_COMMIT= c189207a55da45305c884fe2b50e086fcad4724b GL_TUPLE= https://gitlab.example.com:9434/gitlab:bar:foo-icons:ae7368cab1ca7ca754b38d49da064df87968ffe4:icons/ext/icons \ bar:foo-contrib:9e4dd76ad9b38f33fdb417a4c01935958d5acd2a:contrib CONFIGURE_ARGS= --with-contrib=${WRKSRC_contrib} .... Grouping was used in the previous example with `bar:icons,contrib`. Some redundant information is present with `GL_TUPLE` because grouping is not possible. ==== [[makefile-extract_sufx]] === `EXTRACT_SUFX` If there is one distribution file, and it uses an odd suffix to indicate the compression mechanism, set `EXTRACT_SUFX`. For example, if the distribution file was named [.filename]#foo.tar.gzip# instead of the more normal [.filename]#foo.tar.gz#, write: [.programlisting] .... DISTNAME= foo EXTRACT_SUFX= .tar.gzip .... The `USES=tar[:__xxx__]`, `USES=lha` or `USES=zip` automatically set `EXTRACT_SUFX` to the most common archives extensions as necessary, see crossref:uses[uses,Using `USES` Macros] for more details. If neither of these are set then `EXTRACT_SUFX` defaults to `.tar.gz`. [NOTE] ==== As `EXTRACT_SUFX` is only used in `DISTFILES`, only set one of them.. ==== [[makefile-distfiles-definition]] === `DISTFILES` Sometimes the names of the files to be downloaded have no resemblance to the name of the port. For example, it might be called [.filename]#source.tar.gz# or similar. In other cases the application's source code might be in several different archives, all of which must be downloaded. If this is the case, set `DISTFILES` to be a space separated list of all the files that must be downloaded. [.programlisting] .... DISTFILES= source1.tar.gz source2.tar.gz .... If not explicitly set, `DISTFILES` defaults to `${DISTNAME}${EXTRACT_SUFX}`. [[makefile-extract_only]] === `EXTRACT_ONLY` If only some of the `DISTFILES` must be extracted-for example, one of them is the source code, while another is an uncompressed document-list the filenames that must be extracted in `EXTRACT_ONLY`. [.programlisting] .... DISTFILES= source.tar.gz manual.html EXTRACT_ONLY= source.tar.gz .... When none of the `DISTFILES` need to be uncompressed, set `EXTRACT_ONLY` to the empty string. [.programlisting] .... EXTRACT_ONLY= .... [[porting-patchfiles]] === `PATCHFILES` If the port requires some additional patches that are available by FTP or HTTP, set `PATCHFILES` to the names of the files and `PATCH_SITES` to the URL of the directory that contains them (the format is the same as `MASTER_SITES`). If the patch is not relative to the top of the source tree (that is, `WRKSRC`) because it contains some extra pathnames, set `PATCH_DIST_STRIP` accordingly. For instance, if all the pathnames in the patch have an extra `foozolix-1.0/` in front of the filenames, then set `PATCH_DIST_STRIP=-p1`. Do not worry if the patches are compressed; they will be decompressed automatically if the filenames end with [.filename]#.Z#, [.filename]#.gz#, [.filename]#.bz2# or [.filename]#.xz#. If the patch is distributed with some other files, such as documentation, in a compressed tarball, using `PATCHFILES` is not possible. If that is the case, add the name and the location of the patch tarball to `DISTFILES` and `MASTER_SITES`. Then, use `EXTRA_PATCHES` to point to those files and [.filename]#bsd.port.mk# will automatically apply them. In particular, do _not_ copy patch files into [.filename]#${PATCHDIR}#. That directory may not be writable. [TIP] ==== If there are multiple patches and they need mixed values for the strip parameter, it can be added alongside the patch name in `PATCHFILES`, e.g: [.programlisting] .... PATCHFILES= patch1 patch2:-p1 .... This does not conflict with crossref:makefiles[porting-master-sites-n,the master site grouping feature], adding a group also works: [.programlisting] .... PATCHFILES= patch2:-p1:source2 .... ==== [NOTE] ==== The tarball will have been extracted alongside the regular source by then, so there is no need to explicitly extract it if it is a regular compressed tarball. Take extra care not to overwrite something that already exists in that directory if extracting it manually. Also, do not forget to add a command to remove the copied patch in the `pre-clean` target. ==== [[porting-master-sites-n]] === Multiple Distribution or Patches Files from Multiple Locations (Consider this to be a somewhat "advanced topic"; those new to this document may wish to skip this section at first). This section has information on the fetching mechanism known as both `MASTER_SITES:n` and `MASTER_SITES_NN`. We will refer to this mechanism as `MASTER_SITES:n`. A little background first. OpenBSD has a neat feature inside `DISTFILES` and `PATCHFILES` which allows files and patches to be postfixed with `:n` identifiers. Here, `n` can be any word containing `[0-9a-zA-Z_]` and denote a group designation. For example: [.programlisting] .... DISTFILES= alpha:0 beta:1 .... In OpenBSD, distribution file [.filename]#alpha# will be associated with variable `MASTER_SITES0` instead of our common `MASTER_SITES` and [.filename]#beta# with `MASTER_SITES1`. This is a very interesting feature which can decrease that endless search for the correct download site. Just picture 2 files in `DISTFILES` and 20 sites in `MASTER_SITES`, the sites slow as hell where [.filename]#beta# is carried by all sites in `MASTER_SITES`, and [.filename]#alpha# can only be found in the 20th site. It would be such a waste to check all of them if the maintainer knew this beforehand, would it not? Not a good start for that lovely weekend! Once the concept is clear, just imagine more `DISTFILES` and more `MASTER_SITES`. Surely our "distfiles survey meister" would appreciate the relief to network strain that this would bring. In the next sections, information will follow on the FreeBSD implementation of this idea. We improved a bit on OpenBSD's concept. [IMPORTANT] ==== The group names cannot have dashes in them (`-`), in fact, they cannot have any characters out of the `[a-zA-Z0-9_]` range. This is because, while man:make[1] is ok with variable names containing dashes, man:sh[1] is not. ==== [[porting-master-sites-n-simplified]] ==== Simplified Information This section explains how to quickly prepare fine grained fetching of multiple distribution files and patches from different sites and subdirectories. We describe here a case of simplified `MASTER_SITES:n` usage. This will be sufficient for most scenarios. More detailed information are available in crossref:makefiles[ports-master-sites-n-detailed, Detailed Information]. Some applications consist of multiple distribution files that must be downloaded from a number of different sites. For example, Ghostscript consists of the core of the program, and then a large number of driver files that are used depending on the user's printer. Some of these driver files are supplied with the core, but many others must be downloaded from a variety of different sites. To support this, each entry in `DISTFILES` may be followed by a colon and a "group name". Each site listed in `MASTER_SITES` is then followed by a colon, and the group that indicates which distribution files are downloaded from this site. For example, consider an application with the source split in two parts, [.filename]#source1.tar.gz# and [.filename]#source2.tar.gz#, which must be downloaded from two different sites. The port's [.filename]#Makefile# would include lines like crossref:makefiles[ports-master-sites-n-example-simple-use-one-file-per-site,Simplified Use of `MASTER_SITES:n` with One File Per Site]. [[ports-master-sites-n-example-simple-use-one-file-per-site]] .Simplified Use of `MASTER_SITES:n` with One File Per Site [example] ==== [.programlisting] .... MASTER_SITES= ftp://ftp1.example.com/:source1 \ http://www.example.com/:source2 DISTFILES= source1.tar.gz:source1 \ source2.tar.gz:source2 .... ==== Multiple distribution files can have the same group. Continuing the previous example, suppose that there was a third distfile, [.filename]#source3.tar.gz#, that is downloaded from `www.example.com`. The [.filename]#Makefile# would then be written like crossref:makefiles[ports-master-sites-n-example-simple-use-more-than-one-file-per-site,Simplified Use of `MASTER_SITES:n` with More Than One File Per Site]. [[ports-master-sites-n-example-simple-use-more-than-one-file-per-site]] .Simplified Use of `MASTER_SITES:n` with More Than One File Per Site [example] ==== [.programlisting] .... MASTER_SITES= ftp://ftp.example.com/:source1 \ http://www.example.com/:source2 DISTFILES= source1.tar.gz:source1 \ source2.tar.gz:source2 \ source3.tar.gz:source2 .... ==== [[ports-master-sites-n-detailed]] ==== Detailed Information Okay, so the previous example did not reflect the new port's needs? In this section we will explain in detail how the fine grained fetching mechanism `MASTER_SITES:n` works and how it can be used. . Elements can be postfixed with `:__n__` where _n_ is `[^:,]\+`, that is, _n_ could conceptually be any alphanumeric string but we will limit it to `[a-zA-Z_][0-9a-zA-Z_]+` for now. + Moreover, string matching is case sensitive; that is, `n` is different from `N`. + However, these words cannot be used for postfixing purposes since they yield special meaning: `default`, `all` and `ALL` (they are used internally in item crossref:makefiles[porting-master-sites-n-what-changes-in-port-targets, ii]). Furthermore, `DEFAULT` is a special purpose word (check item crossref:makefiles[porting-master-sites-n-DEFAULT-group,3]). . Elements postfixed with `:n` belong to the group `n`, `:m` belong to group `m` and so forth. + . [[porting-master-sites-n-DEFAULT-group]] Elements without a postfix are groupless, they all belong to the special group `DEFAULT`. Any elements postfixed with `DEFAULT`, is just being redundant unless an element belongs to both `DEFAULT` and other groups at the same time (check item crossref:makefiles[porting-master-sites-n-comma-operator,5]). + These examples are equivalent but the first one is preferred: + [.programlisting] .... MASTER_SITES= alpha .... + [.programlisting] .... MASTER_SITES= alpha:DEFAULT .... . Groups are not exclusive, an element may belong to several different groups at the same time and a group can either have either several different elements or none at all. + . [[porting-master-sites-n-comma-operator]] When an element belongs to several groups at the same time, use the comma operator (`,`). + Instead of repeating it several times, each time with a different postfix, we can list several groups at once in a single postfix. For instance, `:m,n,o` marks an element that belongs to group `m`, `n` and `o`. + All these examples are equivalent but the last one is preferred: + [.programlisting] .... MASTER_SITES= alpha alpha:SOME_SITE .... + [.programlisting] .... MASTER_SITES= alpha:DEFAULT alpha:SOME_SITE .... + [.programlisting] .... MASTER_SITES= alpha:SOME_SITE,DEFAULT .... + [.programlisting] .... MASTER_SITES= alpha:DEFAULT,SOME_SITE .... . All sites within a given group are sorted according to `MASTER_SORT_AWK`. All groups within `MASTER_SITES` and `PATCH_SITES` are sorted as well. + . [[porting-master-sites-n-group-semantics]] Group semantics can be used in any of the variables `MASTER_SITES`, `PATCH_SITES`, `MASTER_SITE_SUBDIR`, `PATCH_SITE_SUBDIR`, `DISTFILES`, and `PATCHFILES` according to this syntax: .. All `MASTER_SITES`, `PATCH_SITES`, `MASTER_SITE_SUBDIR` and `PATCH_SITE_SUBDIR` elements must be terminated with the forward slash `/` character. If any elements belong to any groups, the group postfix `:__n__` must come right after the terminator `/`. The `MASTER_SITES:n` mechanism relies on the existence of the terminator `/` to avoid confusing elements where a `:n` is a valid part of the element with occurrences where `:n` denotes group `n`. For compatibility purposes, since the `/` terminator was not required before in both `MASTER_SITE_SUBDIR` and `PATCH_SITE_SUBDIR` elements, if the postfix immediate preceding character is not a `/` then `:n` will be considered a valid part of the element instead of a group postfix even if an element is postfixed with `:n`. See both crossref:makefiles[ports-master-sites-n-example-detailed-use-master-site-subdir,Detailed Use of `MASTER_SITES:n` in `MASTER_SITE_SUBDIR`] and crossref:makefiles[ports-master-sites-n-example-detailed-use-complete-example-master-sites,Detailed Use of `MASTER_SITES:n` with Comma Operator, Multiple Files, Multiple Sites and Multiple Subdirectories]. + [[ports-master-sites-n-example-detailed-use-master-site-subdir]] .Detailed Use of `MASTER_SITES:n` in `MASTER_SITE_SUBDIR` [example] ==== [.programlisting] .... MASTER_SITE_SUBDIR= old:n new/:NEW .... *** Directories within group `DEFAULT` -> old:n *** Directories within group `NEW` -> new ==== + [[ports-master-sites-n-example-detailed-use-complete-example-master-sites]] .Detailed Use of `MASTER_SITES:n` with Comma Operator, Multiple Files, Multiple Sites and Multiple Subdirectories [example] ==== [.programlisting] .... MASTER_SITES= http://site1/%SUBDIR%/ http://site2/:DEFAULT \ http://site3/:group3 http://site4/:group4 \ http://site5/:group5 http://site6/:group6 \ http://site7/:DEFAULT,group6 \ http://site8/%SUBDIR%/:group6,group7 \ http://site9/:group8 DISTFILES= file1 file2:DEFAULT file3:group3 \ file4:group4,group5,group6 file5:grouping \ file6:group7 MASTER_SITE_SUBDIR= directory-trial:1 directory-n/:groupn \ directory-one/:group6,DEFAULT \ directory .... The previous example results in this fine grained fetching. Sites are listed in the exact order they will be used. *** [.filename]#file1# will be fetched from **** `MASTER_SITE_OVERRIDE` **** http://site1/directory-trial:1/ **** http://site1/directory-one/ **** http://site1/directory/ **** http://site2/ **** http://site7/ **** `MASTER_SITE_BACKUP` *** [.filename]#file2# will be fetched exactly as [.filename]#file1# since they both belong to the same group **** `MASTER_SITE_OVERRIDE` **** http://site1/directory-trial:1/ **** http://site1/directory-one/ **** http://site1/directory/ **** http://site2/ **** http://site7/ **** `MASTER_SITE_BACKUP` *** [.filename]#file3# will be fetched from **** `MASTER_SITE_OVERRIDE` **** http://site3/ **** `MASTER_SITE_BACKUP` *** [.filename]#file4# will be fetched from **** `MASTER_SITE_OVERRIDE` **** http://site4/ **** http://site5/ **** http://site6/ **** http://site7/ **** http://site8/directory-one/ **** `MASTER_SITE_BACKUP` *** [.filename]#file5# will be fetched from **** `MASTER_SITE_OVERRIDE` **** `MASTER_SITE_BACKUP` *** [.filename]#file6# will be fetched from **** `MASTER_SITE_OVERRIDE` **** http://site8/ **** `MASTER_SITE_BACKUP` ==== . How do I group one of the special macros from [.filename]#bsd.sites.mk#, for example, SourceForge (`SF`)? + This has been simplified as much as possible. See crossref:makefiles[ports-master-sites-n-example-detailed-use-master-site-sourceforge,Detailed Use of `MASTER_SITES:n` with SourceForge (`SF`)]. + [[ports-master-sites-n-example-detailed-use-master-site-sourceforge]] .Detailed Use of `MASTER_SITES:n` with SourceForge (`SF`) [example] ==== [.programlisting] .... MASTER_SITES= http://site1/ SF/something/1.0:sourceforge,TEST DISTFILES= something.tar.gz:sourceforge .... [.filename]#something.tar.gz# will be fetched from all sites within SourceForge. ==== . How do I use this with `PATCH*`? + All examples were done with `MASTER*` but they work exactly the same for `PATCH*` ones as can be seen in crossref:makefiles[ports-master-sites-n-example-detailed-use-patch-sites,Simplified Use of `MASTER_SITES:n` with `PATCH_SITES`]. + [[ports-master-sites-n-example-detailed-use-patch-sites]] .Simplified Use of `MASTER_SITES:n` with `PATCH_SITES` [example] ==== [.programlisting] .... PATCH_SITES= http://site1/ http://site2/:test PATCHFILES= patch1:test .... ==== [[port-master-sites-n-what-changed]] ==== What Does Change for Ports? What Does Not? [lowerroman] . All current ports remain the same. The `MASTER_SITES:n` feature code is only activated if there are elements postfixed with `:__n__` like elements according to the aforementioned syntax rules, especially as shown in item crossref:makefiles[porting-master-sites-n-group-semantics, 7]. + . [[porting-master-sites-n-what-changes-in-port-targets]] The port targets remain the same: `checksum`, `makesum`, `patch`, `configure`, `build`, etc. With the obvious exceptions of `do-fetch`, `fetch-list`, `master-sites` and `patch-sites`. ** `do-fetch`: deploys the new grouping postfixed `DISTFILES` and `PATCHFILES` with their matching group elements within both `MASTER_SITES` and `PATCH_SITES` which use matching group elements within both `MASTER_SITE_SUBDIR` and `PATCH_SITE_SUBDIR`. Check crossref:makefiles[ports-master-sites-n-example-detailed-use-complete-example-master-sites,Detailed Use of `MASTER_SITES:n` with Comma Operator, Multiple Files, Multiple Sites and Multiple Subdirectories]. ** `fetch-list`: works like old `fetch-list` with the exception that it groups just like `do-fetch`. ** `master-sites` and `patch-sites`: (incompatible with older versions) only return the elements of group `DEFAULT`; in fact, they execute targets `master-sites-default` and `patch-sites-default` respectively. + Furthermore, using target either `master-sites-all` or `patch-sites-all` is preferred to directly checking either `MASTER_SITES` or `PATCH_SITES`. Also, directly checking is not guaranteed to work in any future versions. Check item crossref:makefiles[porting-master-sites-n-new-port-targets-master-sites-all, B] for more information on these new port targets. . New port targets .. There are `master-sites-_n_` and `patch-sites-_n_` targets which will list the elements of the respective group _n_ within `MASTER_SITES` and `PATCH_SITES` respectively. For instance, both `master-sites-DEFAULT` and `patch-sites-DEFAULT` will return the elements of group `DEFAULT`, `master-sites-test` and `patch-sites-test` of group `test`, and thereon. + .. [[porting-master-sites-n-new-port-targets-master-sites-all]] There are new targets `master-sites-all` and `patch-sites-all` which do the work of the old `master-sites` and `patch-sites` ones. They return the elements of all groups as if they all belonged to the same group with the caveat that it lists as many `MASTER_SITE_BACKUP` and `MASTER_SITE_OVERRIDE` as there are groups defined within either `DISTFILES` or `PATCHFILES`; respectively for `master-sites-all` and `patch-sites-all`. [[makefile-dist_subdir]] === `DIST_SUBDIR` Do not let the port clutter [.filename]#/usr/ports/distfiles#. If the port requires a lot of files to be fetched, or contains a file that has a name that might conflict with other ports (for example, [.filename]#Makefile#), set `DIST_SUBDIR` to the name of the port (`${PORTNAME}` or `${PKGNAMEPREFIX}${PORTNAME}` are fine). This will change `DISTDIR` from the default [.filename]#/usr/ports/distfiles# to [.filename]#/usr/ports/distfiles/${DIST_SUBDIR}#, and in effect puts everything that is required for the port into that subdirectory. It will also look at the subdirectory with the same name on the backup master site at http://distcache.FreeBSD.org[http://distcache.FreeBSD.org] (Setting `DISTDIR` explicitly in [.filename]#Makefile# will not accomplish this, so please use `DIST_SUBDIR`.) [NOTE] ==== This does not affect `MASTER_SITES` defined in the [.filename]#Makefile#. ==== [[makefile-maintainer]] == `MAINTAINER` Set the mail-address here. Please. _:-)_ Only a single address without the comment part is allowed as a `MAINTAINER` value. The format used is `user@hostname.domain`. Please do not include any descriptive text such as a real name in this entry. That merely confuses the Ports infrastructure and most tools using it. The maintainer is responsible for keeping the port up to date and making sure that it works correctly. For a detailed description of the responsibilities of a port maintainer, refer to extref:{contributing}[The challenge for port maintainers, maintain-port]. [NOTE] ==== A maintainer volunteers to keep a port in good working order. Maintainers have the primary responsibility for their ports, but not exclusive ownership. Ports exist for the benefit of the community and, in reality, belong to the community. What this means is that people other than the maintainer can make changes to a port. Large changes to the Ports Collection might require changes to many ports. The FreeBSD Ports Management Team or members of other teams might modify ports to fix dependency issues or other problems, like a version bump for a shared library update. Some types of fixes have "blanket approval" from the {portmgr}, allowing any committer to fix those categories of problems on any port. These fixes do not need approval from the maintainer. Blanket approval for most ports applies to fixes like infrastructure changes, or trivial and _tested_ build and runtime fixes. The current list is available in extref:{committers-guide}[Ports section of the Committer's Guide, ports-qa-misc-blanket-approval]. ==== Other changes to the port will be sent to the maintainer for review and approval before being committed. If the maintainer does not respond to an update request after two weeks (excluding major public holidays), then that is considered a maintainer timeout, and the update can be made without explicit maintainer approval. If the maintainer does not respond within three months, or if there have been three consecutive timeouts, then that maintainer is considered absent without leave, and all of their ports can be assigned back to the pool. Exceptions to this are anything maintained by the {portmgr}, or the {security-officer}. No unauthorized commits may ever be made to ports maintained by those groups. We reserve the right to modify the maintainer's submission to better match existing policies and style of the Ports Collection without explicit blessing from the submitter or the maintainer. Also, large infrastructural changes can result in a port being modified without the maintainer's consent. These kinds of changes will never affect the port's functionality. The {portmgr} reserves the right to revoke or override anyone's maintainership for any reason, and the {security-officer} reserves the right to revoke or override maintainership for security reasons. [[makefile-comment]] == `COMMENT` The comment is a one-line description of a port shown by `pkg info`. Please follow these rules when composing it: . The COMMENT string should be 70 characters or less. . Do _not_ include the package name or version number of software. . The comment must begin with a capital and end without a period. . Do not start with an indefinite article (that is, A or An). . Capitalize names such as Apache, JavaScript, or Perl. . Use a serial comma for lists of words: "green, red, and blue." . Check for spelling errors. Here is an example: [.programlisting] .... COMMENT= Cat chasing a mouse all over the screen .... The COMMENT variable immediately follows the MAINTAINER variable in the [.filename]#Makefile#. [[makefile-www]] == Project website Each port should point to a website that provides more information about the software. Whenever possible, this should be the official project website maintained by the developers of the software. [.programlisting] .... WWW= https://ffmpeg.org/ .... But it can also be a directory or resource in the source code repository: [.programlisting] .... WWW= https://sourceforge.net/projects/mpd/ .... The WWW variable immediately follows the COMMENT variable in the [.filename]#Makefile#. If the same content can be accessed via HTTP and HTTPS, the URL starting with `https://` shall be used. If the URI is the root of the website or directory, it must be terminated with a slash. This information used to be placed into the last line of the [.filename]#pkg-descr# file. It has been moved into the Makefile for easier maintenance and processing. Having a `WWW:` line at the end of the [.filename]#pkg-descr# file is deprecated. [[licenses]] == Licenses Each port must document the license under which it is available. If it is not an OSI approved license it must also document any restrictions on redistribution. [[licenses-license]] === `LICENSE` A short name for the license or licenses if more than one license apply. If it is one of the licenses listed in crossref:makefiles[licenses-license-list,Predefined License List], only `LICENSE_FILE` and `LICENSE_DISTFILES` variables can be set. If this is a license that has not been defined in the ports framework (see crossref:makefiles[licenses-license-list,Predefined License List]), the `LICENSE_PERMS` and `LICENSE_NAME` must be set, along with either `LICENSE_FILE` or `LICENSE_TEXT`. `LICENSE_DISTFILES` and `LICENSE_GROUPS` can also be set, but are not required. The predefined licenses are shown in crossref:makefiles[licenses-license-list,Predefined License List]. The current list is always available in [.filename]#Mk/bsd.licenses.db.mk#. [[licenses-license-ex1]] .Simplest Usage, Predefined Licenses [example] ==== When the [.filename]#README# of some software says "This software is under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version." but does not provide the license file, use this: [.programlisting] .... LICENSE= LGPL21+ .... When the software provides the license file, use this: [.programlisting] .... LICENSE= LGPL21+ LICENSE_FILE= ${WRKSRC}/COPYING .... ==== For the predefined licenses, the default permissions are `dist-mirror dist-sell pkg-mirror pkg-sell auto-accept`. [[licenses-license-list]] .Predefined License List [cols="1,1,1,1", frame="none", options="header"] |=== | Short Name | Name | Group | Permissions |`AGPLv3` |GNU Affero General Public License version 3 |`FSF GPL OSI` |(default) |`AGPLv3+` |GNU Affero General Public License version 3 (or later) |`FSF GPL OSI` |(default) |`APACHE10` |Apache License 1.0 |`FSF` |(default) |`APACHE11` |Apache License 1.1 |`FSF OSI` |(default) |`APACHE20` |Apache License 2.0 |`FSF OSI` |(default) |`ART10` |Artistic License version 1.0 |`OSI` |(default) |`ART20` |Artistic License version 2.0 |`FSF GPL OSI` |(default) |`ARTPERL10` |Artistic License (perl) version 1.0 |`OSI` |(default) |`BSD` |BSD license Generic Version (deprecated) |`FSF OSI COPYFREE` |(default) |`BSD2CLAUSE` |BSD 2-clause "Simplified" License |`FSF OSI COPYFREE` |(default) |`BSD3CLAUSE` |BSD 3-clause "New" or "Revised" License |`FSF OSI COPYFREE` |(default) |`BSD4CLAUSE` |BSD 4-clause "Original" or "Old" License |`FSF` |(default) |`BSL` |Boost Software License |`FSF OSI COPYFREE` |(default) |`CC-BY-1.0` |Creative Commons Attribution 1.0 | |(default) |`CC-BY-2.0` |Creative Commons Attribution 2.0 | |(default) |`CC-BY-2.5` |Creative Commons Attribution 2.5 | |(default) |`CC-BY-3.0` |Creative Commons Attribution 3.0 | |(default) |`CC-BY-4.0` |Creative Commons Attribution 4.0 | |(default) |`CC-BY-NC-1.0` |Creative Commons Attribution Non Commercial 1.0 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-2.0` |Creative Commons Attribution Non Commercial 2.0 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-2.5` |Creative Commons Attribution Non Commercial 2.5 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-3.0` |Creative Commons Attribution Non Commercial 3.0 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-4.0` |Creative Commons Attribution Non Commercial 4.0 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-ND-1.0` |Creative Commons Attribution Non Commercial No Derivatives 1.0 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-ND-2.0` |Creative Commons Attribution Non Commercial No Derivatives 2.0 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-ND-2.5` |Creative Commons Attribution Non Commercial No Derivatives 2.5 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-ND-3.0` |Creative Commons Attribution Non Commercial No Derivatives 3.0 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-ND-4.0` |Creative Commons Attribution Non Commercial No Derivatives 4.0 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-SA-1.0` |Creative Commons Attribution Non Commercial Share Alike 1.0 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-SA-2.0` |Creative Commons Attribution Non Commercial Share Alike 2.0 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-SA-2.5` |Creative Commons Attribution Non Commercial Share Alike 2.5 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-SA-3.0` |Creative Commons Attribution Non Commercial Share Alike 3.0 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-SA-4.0` |Creative Commons Attribution Non Commercial Share Alike 4.0 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-ND-1.0` |Creative Commons Attribution No Derivatives 1.0 | |(default) |`CC-BY-ND-2.0` |Creative Commons Attribution No Derivatives 2.0 | |(default) |`CC-BY-ND-2.5` |Creative Commons Attribution No Derivatives 2.5 | |(default) |`CC-BY-ND-3.0` |Creative Commons Attribution No Derivatives 3.0 | |(default) |`CC-BY-ND-4.0` |Creative Commons Attribution No Derivatives 4.0 | |(default) |`CC-BY-SA-1.0` |Creative Commons Attribution Share Alike 1.0 | |(default) |`CC-BY-SA-2.0` |Creative Commons Attribution Share Alike 2.0 | |(default) |`CC-BY-SA-2.5` |Creative Commons Attribution Share Alike 2.5 | |(default) |`CC-BY-SA-3.0` |Creative Commons Attribution Share Alike 3.0 | |(default) |`CC-BY-SA-4.0` |Creative Commons Attribution Share Alike 4.0 | |(default) |`CC0-1.0` |Creative Commons Zero v1.0 Universal |`FSF GPL COPYFREE` |(default) |`CDDL` |Common Development and Distribution License |`FSF OSI` |(default) |`CPAL-1.0` |Common Public Attribution License |`FSF OSI` |(default) |`ClArtistic` |Clarified Artistic License |`FSF GPL OSI` |(default) |`EPL` |Eclipse Public License |`FSF OSI` |(default) |`GFDL` |GNU Free Documentation License |`FSF` |(default) |`GMGPL` |GNAT Modified General Public License |`FSF GPL OSI` |(default) |`GPLv1` |GNU General Public License version 1 |`FSF GPL OSI` |(default) |`GPLv1+` |GNU General Public License version 1 (or later) |`FSF GPL OSI` |(default) |`GPLv2` |GNU General Public License version 2 |`FSF GPL OSI` |(default) |`GPLv2+` |GNU General Public License version 2 (or later) |`FSF GPL OSI` |(default) |`GPLv3` |GNU General Public License version 3 |`FSF GPL OSI` |(default) |`GPLv3+` |GNU General Public License version 3 (or later) |`FSF GPL OSI` |(default) |`GPLv3RLE` |GNU GPL version 3 Runtime Library Exception |`FSF GPL OSI` |(default) |`GPLv3RLE+` |GNU GPL version 3 Runtime Library Exception (or later) |`FSF GPL OSI` |(default) |`ISCL` |Internet Systems Consortium License |`FSF GPL OSI COPYFREE` |(default) |`LGPL20` |GNU Library General Public License version 2.0 |`FSF GPL OSI` |(default) |`LGPL20+` |GNU Library General Public License version 2.0 (or later) |`FSF GPL OSI` |(default) |`LGPL21` |GNU Lesser General Public License version 2.1 |`FSF GPL OSI` |(default) |`LGPL21+` |GNU Lesser General Public License version 2.1 (or later) |`FSF GPL OSI` |(default) |`LGPL3` |GNU Lesser General Public License version 3 |`FSF GPL OSI` |(default) |`LGPL3+` |GNU Lesser General Public License version 3 (or later) |`FSF GPL OSI` |(default) |`LPPL10` |LaTeX Project Public License version 1.0 |`FSF OSI` |`dist-mirror dist-sell` |`LPPL11` |LaTeX Project Public License version 1.1 |`FSF OSI` |`dist-mirror dist-sell` |`LPPL12` |LaTeX Project Public License version 1.2 |`FSF OSI` |`dist-mirror dist-sell` |`LPPL13` |LaTeX Project Public License version 1.3 |`FSF OSI` |`dist-mirror dist-sell` |`LPPL13a` |LaTeX Project Public License version 1.3a |`FSF OSI` |`dist-mirror dist-sell` |`LPPL13b` |LaTeX Project Public License version 1.3b |`FSF OSI` |`dist-mirror dist-sell` |`LPPL13c` |LaTeX Project Public License version 1.3c |`FSF OSI` |`dist-mirror dist-sell` |`MIT` |MIT license / X11 license |`COPYFREE FSF GPL OSI` |(default) |`MPL10` |Mozilla Public License version 1.0 |`FSF OSI` |(default) |`MPL11` |Mozilla Public License version 1.1 |`FSF OSI` |(default) |`MPL20` |Mozilla Public License version 2.0 |`FSF OSI` |(default) |`NCSA` |University of Illinois/NCSA Open Source License |`COPYFREE FSF GPL OSI` |(default) |`NONE` |No license specified | |`none` |`OFL10` |SIL Open Font License version 1.0 (https://scripts.sil.org/OFL/) |`FONTS` |(default) |`OFL11` |SIL Open Font License version 1.1 (https://scripts.sil.org/OFL/) |`FONTS` |(default) |`OWL` |Open Works License (owl.apotheon.org) |`COPYFREE` |(default) |`OpenSSL` |OpenSSL License |`FSF` |(default) |`PD` |Public Domain |`GPL COPYFREE` |(default) |`PHP202` |PHP License version 2.02 |`FSF OSI` |(default) |`PHP30` |PHP License version 3.0 |`FSF OSI` |(default) |`PHP301` |PHP License version 3.01 |`FSF OSI` |(default) |`PSFL` |Python Software Foundation License |`FSF GPL OSI` |(default) |`PostgreSQL` |PostgreSQL License |`FSF GPL OSI COPYFREE` |(default) |`RUBY` |Ruby License |`FSF` |(default) |`UNLICENSE` |The Unlicense |`COPYFREE FSF GPL` |(default) |`WTFPL` |Do What the Fuck You Want To Public License version 2 |`GPL FSF COPYFREE` |(default) |`WTFPL1` |Do What the Fuck You Want To Public License version 1 |`GPL FSF COPYFREE` |(default) |`ZLIB` |zlib License |`GPL FSF OSI` |(default) |`ZPL21` |Zope Public License version 2.1 |`GPL OSI` |(default) |=== [[licenses-license_perms]] === `LICENSE_PERMS` and `LICENSE_PERMS_NAME_` Permissions. use `none` if empty. .License Permissions List [[licenses-license_perms-dist-mirror]] `dist-mirror`:: Redistribution of the distribution files is permitted. The distribution files will be added to the FreeBSD `MASTER_SITE_BACKUP` CDN. [[licenses-license_perms-no-dist-mirror]] `no-dist-mirror`:: Redistribution of the distribution files is prohibited. This is equivalent to setting crossref:special[porting-restrictions-restricted,`RESTRICTED`]. The distribution files will _not_ be added to the FreeBSD `MASTER_SITE_BACKUP` CDN. [[licenses-license_perms-dist-sell]] `dist-sell`:: Selling of distribution files is permitted. The distribution files will be present on the installer images. [[licenses-license_perms-no-dist-sell]] `no-dist-sell`:: Selling of distribution files is prohibited. This is equivalent to setting crossref:special[porting-restrictions-no_cdrom,`NO_CDROM`]. [[licenses-license_perms-pkg-mirror]] `pkg-mirror`:: Free redistribution of package is permitted. The package will be distributed on the FreeBSD package CDN https://pkg.freebsd.org/[https://pkg.freebsd.org/]. [[licenses-license_perms-no-pkg-mirror]] `no-pkg-mirror`:: Free redistribution of package is prohibited. Equivalent to setting crossref:special[porting-restrictions-no_package,`NO_PACKAGE`]. The package will _not_ be distributed from the FreeBSD package CDN https://pkg.freebsd.org/[https://pkg.freebsd.org/]. [[licenses-license_perms-pkg-sell]] `pkg-sell`:: Selling of package is permitted. The package will be present on the installer images. [[licenses-license_perms-no-pkg-sell]] `no-pkg-sell`:: Selling of package is prohibited. This is equivalent to setting crossref:special[porting-restrictions-no_cdrom,`NO_CDROM`]. The package will _not_ be present on the installer images. [[licenses-license_perms-auto-accept]] `auto-accept`:: License is accepted by default. Prompts to accept a license are not displayed unless the user has defined `LICENSES_ASK`. Use this unless the license states the user must accept the terms of the license. [[licenses-license_perms-no-auto-accept]] `no-auto-accept`:: License is not accepted by default. The user will always be asked to confirm the acceptance of this license. This must be used if the license states that the user must accept its terms. When both `_permission_` and `no-_permission_` is present the `no-_permission_` will cancel `_permission_`. When `_permission_` is not present, it is considered to be a `no-_permission_`. [WARNING] ==== Some missing permissions will prevent a port (and all ports depending on it) from being usable by package users: A port without the `auto-accept` permission will never be built and all the ports depending on it will be ignored. A port without the `pkg-mirror` permission, and any ports that depend on it, will be removed after the build, thus ensuring they are not distributed. ==== [[licenses-license_perms-ex1]] .Nonstandard License [example] ==== Read the terms of the license and translate those using the available permissions. [.programlisting] .... LICENSE= UNKNOWN LICENSE_NAME= unknown LICENSE_TEXT= This program is NOT in public domain.\ It can be freely distributed for non-commercial purposes only. LICENSE_PERMS= dist-mirror no-dist-sell pkg-mirror no-pkg-sell auto-accept .... ==== [[licenses-license_perms-ex2]] .Standard and Nonstandard Licenses [example] ==== Read the terms of the license and express those using the available permissions. In case of doubt, please ask for guidance on the {freebsd-ports}. [.programlisting] .... LICENSE= WARSOW GPLv2 LICENSE_COMB= multi LICENSE_NAME_WARSOW= Warsow Content License LICENSE_FILE_WARSOW= ${WRKSRC}/docs/license.txt LICENSE_PERMS_WARSOW= dist-mirror pkg-mirror auto-accept .... When the permissions of the GPLv2 and the UNKNOWN licenses are mixed, the port ends up with `dist-mirror dist-sell pkg-mirror pkg-sell auto-accept dist-mirror no-dist-sell pkg-mirror no-pkg-sell auto-accept`. The `no-_permissions_` cancel the _permissions_. The resulting list of permissions are _dist-mirror pkg-mirror auto-accept_. The distribution files and the packages will not be available on the installer images. ==== [[licenses-license_groups]] === `LICENSE_GROUPS` and `LICENSE_GROUPS_NAME` Groups the license belongs. .Predefined License Groups List [[licenses-license_groups-FSF]] `FSF`:: Free Software Foundation Approved, see the https://www.fsf.org/licensing/[FSF Licensing & Compliance Team]. [[licenses-license_groups-GPL]] `GPL`:: GPL Compatible [[licenses-license_groups-OSI]] `OSI`:: OSI Approved, see the Open Source Initiative https://opensource.org/licenses/[Open Source Licenses] page. [[licenses-license_groups-COPYFREE]] `COPYFREE`:: Comply with Copyfree Standard Definition, see the https://copyfree.org/standard/licenses/[Copyfree Licenses] page. [[licenses-license_groups-FONTS]] `FONTS`:: Font licenses [[licenses-license_name]] === `LICENSE_NAME` and `LICENSE_NAME_NAME` Full name of the license. [[licenses-license_name-ex1]] .`LICENSE_NAME` [example] ==== [.programlisting] .... LICENSE= UNRAR LICENSE_NAME= UnRAR License LICENSE_FILE= ${WRKSRC}/license.txt LICENSE_PERMS= dist-mirror dist-sell pkg-mirror pkg-sell auto-accept .... ==== [[licenses-license_file]] === `LICENSE_FILE` and `LICENSE_FILE_NAME` Full path to the file containing the license text, usually [.filename]#${WRKSRC}/some/file#. If the file is not in the distfile, and its content is too long to be put in crossref:makefiles[licenses-license_text,`LICENSE_TEXT`], put it in a new file in [.filename]#${FILESDIR}#. [[licenses-license_file-ex1]] .`LICENSE_FILE` [example] ==== [.programlisting] .... LICENSE= GPLv3+ LICENSE_FILE= ${WRKSRC}/COPYING .... ==== [[licenses-license_text]] === `LICENSE_TEXT` and `LICENSE_TEXT_NAME` Text to use as a license. Useful when the license is not in the distribution files and its text is short. [[licenses-license_text-ex1]] .`LICENSE_TEXT` [example] ==== [.programlisting] .... LICENSE= UNKNOWN LICENSE_NAME= unknown LICENSE_TEXT= This program is NOT in public domain.\ It can be freely distributed for non-commercial purposes only,\ and THERE IS NO WARRANTY FOR THIS PROGRAM. LICENSE_PERMS= dist-mirror no-dist-sell pkg-mirror no-pkg-sell auto-accept .... ==== [[licenses-license_distfiles]] === `LICENSE_DISTFILES` and `LICENSE_DISTFILES_NAME` The distribution files to which the licenses apply. Defaults to all the distribution files. [[licenses-license_distfiles-ex1]] .`LICENSE_DISTFILES` [example] ==== Used when the distribution files do not all have the same license. For example, one has a code license, and another has some artwork that cannot be redistributed: [.programlisting] .... MASTER_SITES= SF/some-game DISTFILES= ${DISTNAME}${EXTRACT_SUFX} artwork.zip LICENSE= BSD3CLAUSE ARTWORK LICENSE_COMB= dual LICENSE_NAME_ARTWORK= The game artwork license LICENSE_TEXT_ARTWORK= The README says that the files cannot be redistributed LICENSE_PERMS_ARTWORK= pkg-mirror pkg-sell auto-accept LICENSE_DISTFILES_BSD3CLAUSE= ${DISTNAME}${EXTRACT_SUFX} LICENSE_DISTFILES_ARTWORK= artwork.zip .... ==== [[licenses-license_comb]] === `LICENSE_COMB` Set to `multi` if all licenses apply. Set to `dual` if any license applies. Defaults to `single`. [[licenses-license_comb-ex1]] .Dual Licenses [example] ==== When a port says "This software may be distributed under the GNU General Public License or the Artistic License", it means that either license can be used. Use this: [.programlisting] .... LICENSE= ART10 GPLv1 LICENSE_COMB= dual .... If license files are provided, use this: [.programlisting] .... LICENSE= ART10 GPLv1 LICENSE_COMB= dual LICENSE_FILE_ART10= ${WRKSRC}/Artistic LICENSE_FILE_GPLv1= ${WRKSRC}/Copying .... ==== [[licenses-license_comb-ex2]] .Multiple Licenses [example] ==== When part of a port has one license, and another part has a different license, use `multi`: [.programlisting] .... LICENSE= GPLv2 LGPL21+ LICENSE_COMB= multi .... ==== [[makefile-portscout]] == `PORTSCOUT` Portscout is an automated distfile check utility for the FreeBSD Ports Collection, described in detail in crossref:keeping-up[distfile-survey,Portscout: the FreeBSD Ports Distfile Scanner]. `PORTSCOUT` defines special conditions within which the Portscout distfile scanner is restricted. Situations where `PORTSCOUT` is set include: * When distfiles have to be ignored for specific versions. For example, to exclude version _8.2_ and version _8.3_ from distfile version checks because they are known to be broken, add: + [.programlisting] .... PORTSCOUT= skipv:8.2,8.3 .... * When distfile version checks have to be disabled completely. For example, if a port is not going to be updated ever again, add: + [.programlisting] .... PORTSCOUT= ignore:1 .... * When specific versions or specific major and minor revisions of a distfile must be checked. For example, if only version _0.6.4_ must be monitored because newer versions have compatibility issues with FreeBSD, add: + [.programlisting] .... PORTSCOUT= limit:^0\.6\.4 .... * When URLs listing the available versions differ from the download URLs. For example, to limit distfile version checks to the download page for the package:databases/pgtune[] port, add: + [.programlisting] .... PORTSCOUT= site:http://www.renpy.org/dl/release/ .... [[makefile-depend]] == Dependencies Many ports depend on other ports. This is a very convenient feature of most Unix-like operating systems, including FreeBSD. Multiple ports can share a common dependency, rather than bundling that dependency with every port or package that needs it. There are seven variables that can be used to ensure that all the required bits will be on the user's machine. There are also some pre-supported dependency variables for common cases, plus a few more to control the behavior of dependencies. [IMPORTANT] ==== When software has extra dependencies that provide extra features, the base dependencies listed in `*_DEPENDS` should include the extra dependencies that would benefit most users. The base dependencies should never be a "minimal" dependency set. The goal is not to include every dependency possible. Only include those that will benefit most people. ==== [[makefile-lib_depends]] === `LIB_DEPENDS` This variable specifies the shared libraries this port depends on. It is a list of `_lib:dir_` tuples where `_lib_` is the name of the shared library, `_dir_` is the directory in which to find it in case it is not available. For example, [.programlisting] .... LIB_DEPENDS= libjpeg.so:graphics/jpeg .... will check for a shared jpeg library with any version, and descend into the [.filename]#graphics/jpeg# subdirectory of the ports tree to build and install it if it is not found. The dependency is checked twice, once from within the `build` target and then from within the `install` target. Also, the name of the dependency is put into the package so that `pkg install` (see man:pkg-install[8]) will automatically install it if it is not on the user's system. [[makefile-run_depends]] === `RUN_DEPENDS` This variable specifies executables or files this port depends on during run-time. It is a list of ``_path:dir_``[:``_target_``] tuples where `_path_` is the name of the executable or file, _dir_ is the directory in which to find it in case it is not available, and _target_ is the target to call in that directory. If _path_ starts with a slash (`/`), it is treated as a file and its existence is tested with `test -e`; otherwise, it is assumed to be an executable, and `which -s` is used to determine if the program exists in the search path. For example, [.programlisting] .... RUN_DEPENDS= ${LOCALBASE}/news/bin/innd:news/inn \ xmlcatmgr:textproc/xmlcatmgr .... will check if the file or directory [.filename]#/usr/local/news/bin/innd# exists, and build and install it from the [.filename]#news/inn# subdirectory of the ports tree if it is not found. It will also see if an executable called `xmlcatmgr` is in the search path, and descend into [.filename]#textproc/xmlcatmgr# to build and install it if it is not found. [NOTE] ==== In this case, `innd` is actually an executable; if an executable is in a place that is not expected to be in the search path, use the full pathname. ==== [NOTE] ==== The official search `PATH` used on the ports build cluster is [.programlisting] .... /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin .... ==== The dependency is checked from within the `install` target. Also, the name of the dependency is put into the package so that `pkg install` (see man:pkg-install[8]) will automatically install it if it is not on the user's system. The _target_ part can be omitted if it is the same as `DEPENDS_TARGET`. A quite common situation is when `RUN_DEPENDS` is literally the same as `BUILD_DEPENDS`, especially if ported software is written in a scripted language or if it requires the same build and run-time environment. In this case, it is both tempting and intuitive to directly assign one to the other: [.programlisting] .... RUN_DEPENDS= ${BUILD_DEPENDS} .... However, such assignment can pollute run-time dependencies with entries not defined in the port's original `BUILD_DEPENDS`. This happens because of man:make[1]'s lazy evaluation of variable assignment. Consider a [.filename]#Makefile# with `USE_*`, which are processed by [.filename]#ports/Mk/bsd.*.mk# to augment initial build dependencies. For example, `USES= gmake` adds package:devel/gmake[] to `BUILD_DEPENDS`. To prevent such additional dependencies from polluting `RUN_DEPENDS`, create another variable with the current content of `BUILD_DEPENDS` and assign it to both `BUILD_DEPENDS` and `RUN_DEPENDS`: [.programlisting] .... MY_DEPENDS= some:devel/some \ other:lang/other BUILD_DEPENDS= ${MY_DEPENDS} RUN_DEPENDS= ${MY_DEPENDS} .... [IMPORTANT] ==== _Do not_ use `:=` to assign `BUILD_DEPENDS` to `RUN_DEPENDS` or vice-versa. All variables are expanded immediately, which is exactly the wrong thing to do and almost always a failure. ==== [[makefile-build_depends]] === `BUILD_DEPENDS` This variable specifies executables or files this port requires to build. Like `RUN_DEPENDS`, it is a list of ``_path:dir_``[:``_target_``] tuples. For example, [.programlisting] .... BUILD_DEPENDS= unzip:archivers/unzip .... will check for an executable called `unzip`, and descend into the [.filename]#archivers/unzip# subdirectory of the ports tree to build and install it if it is not found. [NOTE] ==== "build" here means everything from extraction to compilation. The dependency is checked from within the `extract` target. The _target_ part can be omitted if it is the same as `DEPENDS_TARGET` ==== [[makefile-fetch_depends]] === `FETCH_DEPENDS` This variable specifies executables or files this port requires to fetch. Like the previous two, it is a list of ``_path:dir_``[:``_target_``] tuples. For example, [.programlisting] .... FETCH_DEPENDS= ncftp2:net/ncftp2 .... will check for an executable called `ncftp2`, and descend into the [.filename]#net/ncftp2# subdirectory of the ports tree to build and install it if it is not found. The dependency is checked from within the `fetch` target. The _target_ part can be omitted if it is the same as `DEPENDS_TARGET`. [[makefile-extract_depends]] === `EXTRACT_DEPENDS` This variable specifies executables or files this port requires for extraction. Like the previous, it is a list of ``_path:dir_``[:``_target_``] tuples. For example, [.programlisting] .... EXTRACT_DEPENDS= unzip:archivers/unzip .... will check for an executable called `unzip`, and descend into the [.filename]#archivers/unzip# subdirectory of the ports tree to build and install it if it is not found. The dependency is checked from within the `extract` target. The _target_ part can be omitted if it is the same as `DEPENDS_TARGET`. [NOTE] ==== Use this variable only if the extraction does not already work (the default assumes `tar`) and cannot be made to work using `USES=tar`, `USES=lha` or `USES=zip` described in crossref:uses[uses,Using `USES` Macros]. ==== [[makefile-patch_depends]] === `PATCH_DEPENDS` This variable specifies executables or files this port requires to patch. Like the previous, it is a list of ``_path:dir_``[:``_target_``] tuples. For example, [.programlisting] .... PATCH_DEPENDS= ${NONEXISTENT}:java/jfc:extract .... will descend into the [.filename]#java/jfc# subdirectory of the ports tree to extract it. The dependency is checked from within the `patch` target. The _target_ part can be omitted if it is the same as `DEPENDS_TARGET`. [[makefile-uses]] === `USES` Parameters can be added to define different features and dependencies used by the port. They are specified by adding this line to the [.filename]#Makefile#: [.programlisting] .... USES= feature[:arguments] .... For the complete list of values, please see crossref:uses[uses,Using `USES` Macros]. [WARNING] ==== `USES` cannot be assigned after inclusion of [.filename]#bsd.port.pre.mk#. ==== [[makefile-use-vars]] === `USE_*` Several variables exist to define common dependencies shared by many ports. Their use is optional, but helps to reduce the verbosity of the port [.filename]##Makefile##s. Each of them is styled as `USE_*`. These variables may be used only in the port [.filename]##Makefile##s and [.filename]#ports/Mk/bsd.*.mk#. They are not meant for user-settable options - use `PORT_OPTIONS` for that purpose. [NOTE] ==== It is _always_ incorrect to set any `USE_*` in [.filename]#/etc/make.conf#. For instance, setting [.programlisting] .... USE_GCC=X.Y .... (where X.Y is version number) would add a dependency on gccXY for every port, including `lang/gccXY` itself! ==== [[makefile-use-vars-table]] .`USE_*` [cols="1,1", frame="none", options="header"] |=== | Variable | Means |`USE_GCC` a| The port requires GCC (`gcc` or `{g-plus-plus}`) to build. Some ports need a specific, old GCC version, some require modern, recent versions. It is typically set to `yes` (means always use stable, modern GCC from ports per `GCC_DEFAULT` in [.filename]#Mk/bsd.default-versions.mk#). This is also the default value. The exact version can also be specified, with a value such as `10`. GCC from the base system is used when it satisfies the requested version, otherwise an appropriate compiler is built from ports, and `CC` and `CXX` are adjusted accordingly. The `:build` argument following the version specifier adds only a build time dependency to the port. For example: [example] ==== [.programlisting] .... USE_GCC=yes # port requires a current version of GCC USE_GCC=11:build # port requires GCC 11 at build time only .... ==== [NOTE] ==== `USE_GCC=any` is deprecated and should not be used in new ports ==== |=== Variables related to gmake and [.filename]#configure# are described in crossref:special[building,Building Mechanisms], while autoconf, automake and libtool are described in crossref:special[using-autotools,Using GNU Autotools]. Perl related variables are described in crossref:special[using-perl,Using Perl]. X11 variables are listed in crossref:special[using-x11,Using X11]. crossref:special[using-gnome,Using Gnome] deals with GNOME and crossref:special[using-kde,Using KDE] with KDE related variables. crossref:special[using-java,Using Java] documents Java variables, while crossref:special[using-php,Web Applications, Apache and PHP] contains information on Apache, PHP and PEAR modules. Python is discussed in crossref:special[using-python,Using Python], while Ruby in crossref:uses[uses-ruby,Ruby]. crossref:special[using-sdl,Using SDL] provides variables used for SDL applications and finally, crossref:special[using-xfce,Using Xfce] contains information on Xfce. [[makefile-version-dependency]] === Minimal Version of a Dependency A minimal version of a dependency can be specified in any `*_DEPENDS` except `LIB_DEPENDS` using this syntax: [.programlisting] .... p5-Spiffy>=0.26:devel/p5-Spiffy .... The first field contains a dependent package name, which must match the entry in the package database, a comparison sign, and a package version. The dependency is satisfied if p5-Spiffy-0.26 or newer is installed on the machine. [[makefile-note-on-dependencies]] === Notes on Dependencies As mentioned above, the default target to call when a dependency is required is `DEPENDS_TARGET`. It defaults to `install`. This is a user variable; it is never defined in a port's [.filename]#Makefile#. If the port needs a special way to handle a dependency, use the `:target` part of `*_DEPENDS` instead of redefining `DEPENDS_TARGET`. When running `make clean`, the port dependencies are automatically cleaned too. If this is not desirable, define `NOCLEANDEPENDS` in the environment. This may be particularly desirable if the port has something that takes a long time to rebuild in its dependency list, such as KDE, GNOME or Mozilla. To depend on another port unconditionally, use the variable `${NONEXISTENT}` as the first field of `BUILD_DEPENDS` or `RUN_DEPENDS`. Use this only when the source of the other port is needed. Compilation time can be saved by specifying the target too. For instance [.programlisting] .... BUILD_DEPENDS= ${NONEXISTENT}:graphics/jpeg:extract .... will always descend to the `jpeg` port and extract it. [[makefile-circular-dependencies]] === Circular Dependencies Are Fatal [IMPORTANT] ==== Do not introduce any circular dependencies into the ports tree! ==== The ports building technology does not tolerate circular dependencies. If one is introduced, someone, somewhere in the world, will have their FreeBSD installation broken almost immediately, with many others quickly to follow. These can really be hard to detect. If in doubt, before making that change, make sure to run: `cd /usr/ports; make index`. That process can be quite slow on older machines, but it may be able to save a large number of people, including yourself, a lot of grief in the process. [[makefile-automatic-dependencies]] === Problems Caused by Automatic Dependencies Dependencies must be declared either explicitly or by using the crossref:makefiles[makefile-options,OPTIONS framework]. Using other methods like automatic detection complicates indexing, which causes problems for port and package management. [[makefile-automatic-dependencies-bad]] .Wrong Declaration of an Optional Dependency [example] ==== [.programlisting] .... .include .if exists(${LOCALBASE}/bin/foo) LIB_DEPENDS= libbar.so:foo/bar .endif .... ==== The problem with trying to automatically add dependencies is that files and settings outside an individual port can change at any time. For example: an index is built, then a batch of ports are installed. But one of the ports installs the tested file. The index is now incorrect, because an installed port unexpectedly has a new dependency. The index may still be wrong even after rebuilding if other ports also determine their need for dependencies based on the existence of other files. [[makefile-automatic-dependencies-good]] .Correct Declaration of an Optional Dependency [example] ==== [.programlisting] .... OPTIONS_DEFINE= BAR BAR_DESC= Calling cellphones via bar BAR_LIB_DEPENDS= libbar.so:foo/bar .... ==== Testing option variables is the correct method. It will not cause inconsistencies in the index of a batch of ports, provided the options were defined prior to the index build. Simple scripts can then be used to automate the building, installation, and updating of these ports and their packages. [[makefile-masterdir]] == Slave Ports and `MASTERDIR` If the port needs to build slightly different versions of packages by having a variable (for instance, resolution, or paper size) take different values, create one subdirectory per package to make it easier for users to see what to do, but try to share as many files as possible between ports. Typically, by using variables cleverly, only a very short [.filename]#Makefile# is needed in all but one of the directories. In the sole [.filename]#Makefile#, use `MASTERDIR` to specify the directory where the rest of the files are. Also, use a variable as part of crossref:makefiles[porting-pkgname,`PKGNAMESUFFIX`] so the packages will have different names. This will be best demonstrated by an example. This is part of [.filename]#print/pkfonts300/Makefile#; [.programlisting] .... PORTNAME= pkfonts${RESOLUTION} PORTVERSION= 1.0 DISTFILES= pk${RESOLUTION}.tar.gz PLIST= ${PKGDIR}/pkg-plist.${RESOLUTION} .if !defined(RESOLUTION) RESOLUTION= 300 .else .if ${RESOLUTION} != 118 && ${RESOLUTION} != 240 && \ ${RESOLUTION} != 300 && ${RESOLUTION} != 360 && \ ${RESOLUTION} != 400 && ${RESOLUTION} != 600 .BEGIN: @${ECHO_MSG} "Error: invalid value for RESOLUTION: \"${RESOLUTION}\"" @${ECHO_MSG} "Possible values are: 118, 240, 300, 360, 400 and 600." @${FALSE} .endif .endif .... package:print/pkfonts300[] also has all the regular patches, package files, etc. Running `make` there, it will take the default value for the resolution (300) and build the port normally. As for other resolutions, this is the _entire_ [.filename]#print/pkfonts360/Makefile#: [.programlisting] .... RESOLUTION= 360 MASTERDIR= ${.CURDIR}/../pkfonts300 .include "${MASTERDIR}/Makefile" .... ([.filename]#print/pkfonts118/Makefile#, [.filename]#print/pkfonts600/Makefile#, and all the other are similar). `MASTERDIR` definition tells [.filename]#bsd.port.mk# that the regular set of subdirectories like `FILESDIR` and `SCRIPTDIR` are to be found under [.filename]#pkfonts300#. The `RESOLUTION=360` line will override the `RESOLUTION=300` line in [.filename]#pkfonts300/Makefile# and the port will be built with resolution set to 360. [[makefile-manpages]] == Man Pages If the port anchors its man tree somewhere other than `PREFIX`, use `MANDIRS` to specify those directories. Note that the files corresponding to manual pages must be placed in [.filename]#pkg-plist# along with the rest of the files. The purpose of `MANDIRS` is to enable automatic compression of manual pages, therefore the file names are suffixed with [.filename]#.gz#. [[makefile-info]] == Info Files If the package needs to install GNU info files, list them in `INFO` (without the trailing `.info`), one entry per document. These files are assumed to be installed to [.filename]#PREFIX/INFO_PATH#. Change `INFO_PATH` if the package uses a different location. However, this is not recommended. These entries contain just the path relative to [.filename]#PREFIX/INFO_PATH#. For example, package:lang/gcc34[] installs info files to [.filename]#PREFIX/INFO_PATH/gcc34#, and `INFO` will be something like this: [.programlisting] .... INFO= gcc34/cpp gcc34/cppinternals gcc34/g77 ... .... Appropriate installation/de-installation code will be automatically added to the temporary [.filename]#pkg-plist# before package registration. [[makefile-options]] == Makefile Options Many applications can be built with optional or differing configurations. Examples include choice of natural (human) language, GUI versus command-line, or type of database to support. Users may need a different configuration than the default, so the ports system provides hooks the port author can use to control which variant will be built. Supporting these options properly will make users happy, and effectively provide two or more ports for the price of one. [[makefile-options-options]] === `OPTIONS` [[makefile-options-background]] ==== Background `OPTIONS_*` give the user installing the port a dialog showing the available options, and then saves those options to [.filename]#${PORT_DBDIR}/${OPTIONS_NAME}/options#. The next time the port is built, the options are reused. `PORT_DBDIR` defaults to [.filename]#/var/db/ports#. `OPTIONS_NAME` is to the port origin with an underscore as the space separator, for example, for package:dns/bind99[] it will be `dns_bind99`. When the user runs `make config` (or runs `make build` for the first time), the framework checks for [.filename]#${PORT_DBDIR}/${OPTIONS_NAME}/options#. If that file does not exist, the values of `OPTIONS_*` are used, and a dialog box is displayed where the options can be enabled or disabled. Then [.filename]#options# is saved and the configured variables are used when building the port. If a new version of the port adds new `OPTIONS`, the dialog will be presented to the user with the saved values of old `OPTIONS` prefilled. `make showconfig` shows the saved configuration. Use `make rmconfig` to remove the saved configuration. [[makefile-options-syntax]] ==== Syntax `OPTIONS_DEFINE` contains a list of `OPTIONS` to be used. These are independent of each other and are not grouped: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 .... Once defined, `OPTIONS` are described (optional, but strongly recommended): [.programlisting] .... OPT1_DESC= Describe OPT1 OPT2_DESC= Describe OPT2 OPT3_DESC= Describe OPT3 OPT4_DESC= Describe OPT4 OPT5_DESC= Describe OPT5 OPT6_DESC= Describe OPT6 .... [.filename]#ports/Mk/bsd.options.desc.mk# has descriptions for many common `OPTIONS`. While often useful, override them if the description is insufficient for the port. [TIP] ==== When describing options, view it from the perspective of the user: "What functionality does it change?" and "Why would I want to enable this?" Do not just repeat the name. For example, describing the `NLS` option as "include NLS support" does not help the user, who can already see the option name but may not know what it means. Describing it as "Native Language Support via gettext utilities" is much more helpful. ==== [IMPORTANT] ==== Option names are always in all uppercase. They cannot use mixed case or lowercase. ==== `OPTIONS` can be grouped as radio choices, where only one choice from each group is allowed: [.programlisting] .... OPTIONS_SINGLE= SG1 OPTIONS_SINGLE_SG1= OPT3 OPT4 .... [WARNING] ==== There _must_ be one of each `OPTIONS_SINGLE` group selected at all times for the options to be valid. One option of each group _must_ be added to `OPTIONS_DEFAULT`. ==== `OPTIONS` can be grouped as radio choices, where none or only one choice from each group is allowed: [.programlisting] .... OPTIONS_RADIO= RG1 OPTIONS_RADIO_RG1= OPT7 OPT8 .... `OPTIONS` can also be grouped as "multiple-choice" lists, where _at least one_ option must be enabled: [.programlisting] .... OPTIONS_MULTI= MG1 OPTIONS_MULTI_MG1= OPT5 OPT6 .... `OPTIONS` can also be grouped as "multiple-choice" lists, where none or any option can be enabled: [.programlisting] .... OPTIONS_GROUP= GG1 OPTIONS_GROUP_GG1= OPT9 OPT10 .... `OPTIONS` are unset by default, unless they are listed in `OPTIONS_DEFAULT`: [.programlisting] .... OPTIONS_DEFAULT= OPT1 OPT3 OPT6 .... `OPTIONS` definitions must appear before the inclusion of [.filename]#bsd.port.options.mk#. `PORT_OPTIONS` values can only be tested after the inclusion of [.filename]#bsd.port.options.mk#. Inclusion of [.filename]#bsd.port.pre.mk# can be used instead, too, and is still widely used in ports written before the introduction of [.filename]#bsd.port.options.mk#. But be aware that some variables will not work as expected after the inclusion of [.filename]#bsd.port.pre.mk#, typically some `USE_*` flags. [[ports-options-simple-use]] .Simple Use of `OPTIONS` [example] ==== [.programlisting] .... OPTIONS_DEFINE= FOO BAR OPTIONS_DEFAULT=FOO FOO_DESC= Option foo support BAR_DESC= Feature bar support # Will add --with-foo / --without-foo FOO_CONFIGURE_WITH= foo BAR_RUN_DEPENDS= bar:bar/bar .include .... ==== [[ports-options-check-unset]] .Check for Unset Port `OPTIONS` [example] ==== [.programlisting] .... .if ! ${PORT_OPTIONS:MEXAMPLES} CONFIGURE_ARGS+=--without-examples .endif .... The form shown above is discouraged. The preferred method is using a configure knob to really enable and disable the feature to match the option: [.programlisting] .... # Will add --with-examples / --without-examples EXAMPLES_CONFIGURE_WITH= examples .... ==== [[ports-options-practical-use]] .Practical Use of `OPTIONS` [example] ==== [.programlisting] .... OPTIONS_DEFINE= EXAMPLES OPTIONS_DEFAULT= PGSQL LDAP SSL OPTIONS_SINGLE= BACKEND OPTIONS_SINGLE_BACKEND= MYSQL PGSQL BDB OPTIONS_MULTI= AUTH OPTIONS_MULTI_AUTH= LDAP PAM SSL EXAMPLES_DESC= Install extra examples MYSQL_DESC= Use MySQL as backend PGSQL_DESC= Use PostgreSQL as backend BDB_DESC= Use Berkeley DB as backend LDAP_DESC= Build with LDAP authentication support PAM_DESC= Build with PAM support SSL_DESC= Build with OpenSSL support # Will add USE_PGSQL=yes PGSQL_USE= pgsql=yes # Will add --enable-postgres / --disable-postgres PGSQL_CONFIGURE_ENABLE= postgres ICU_LIB_DEPENDS= libicuuc.so:devel/icu # Will add --with-examples / --without-examples EXAMPLES_CONFIGURE_WITH= examples # Check other OPTIONS .include .... ==== [[makefile-options-default]] ==== Default Options These options are always on by default. * `DOCS` - build and install documentation. * `NLS` - Native Language Support. * `EXAMPLES` - build and install examples. * `IPV6` - IPv6 protocol support. [NOTE] ==== There is no need to add these to `OPTIONS_DEFAULT`. To have them active, and show up in the options selection dialog, however, they must be added to `OPTIONS_DEFINE`. ==== [[makefile-options-auto-activation]] === Feature Auto-Activation When using a GNU configure script, keep an eye on which optional features are activated by auto-detection. Explicitly disable optional features that are not needed by adding `--without-xxx` or `--disable-xxx` in `CONFIGURE_ARGS`. [[makefile-options-auto-activation-bad]] .Wrong Handling of an Option [example] ==== [.programlisting] .... .if ${PORT_OPTIONS:MFOO} LIB_DEPENDS+= libfoo.so:devel/foo CONFIGURE_ARGS+= --enable-foo .endif .... ==== In the example above, imagine a library libfoo is installed on the system. The user does not want this application to use libfoo, so he toggled the option off in the `make config` dialog. But the application's configure script detects the library present in the system and includes its support in the resulting executable. Now when the user decides to remove libfoo from the system, the ports system does not protest (no dependency on libfoo was recorded) but the application breaks. [[makefile-options-auto-activation-good]] .Correct Handling of an Option [example] ==== [.programlisting] .... FOO_LIB_DEPENDS= libfoo.so:devel/foo # Will add --enable-foo / --disable-foo FOO_CONFIGURE_ENABLE= foo .... ==== [NOTE] ==== Under some circumstances, the shorthand conditional syntax can cause problems with complex constructs. The errors are usually `Malformed conditional`, an alternative syntax can be used. [.programlisting] .... .if !empty(VARIABLE:MVALUE) .... as an alternative to [.programlisting] .... .if ${VARIABLE:MVALUE} .... ==== [[options-helpers]] === Options Helpers There are some macros to help simplify conditional values which differ based on the options set. For easier access, a comprehensive list is provided: `PLIST_SUB`, `SUB_LIST`:: For automatic `%%_OPT_%%` and `%%NO__OPT__%%` generation, see crossref:makefiles[options_sub, `OPTIONS_SUB`]. + For more complex usage, see crossref:makefiles[options-variables, Generic Variables Replacement, `OPT_VARIABLE` and `OPT_VARIABLE_OFF`]. `CONFIGURE_ARGS`:: For `--enable-_x_` and `--disable-_x_`, see crossref:makefiles[options-configure_enable, `OPT_CONFIGURE_ENABLE`]. + For `--with-_x_` and `--without-_x_`, see crossref:makefiles[options-configure_with, `OPT_CONFIGURE_WITH`]. + For all other cases, see crossref:makefiles[options-configure_on, `OPT_CONFIGURE_ON` and `OPT_CONFIGURE_OFF`]. `CMAKE_ARGS`:: For arguments that are booleans (`on`, `off`, `true`, `false`, `0`, `1`) see crossref:makefiles[options-cmake_bool, `OPT_CMAKE_BOOL` and `OPT_CMAKE_BOOL_OFF`]. + For all other cases, see crossref:makefiles[options-cmake_on, `OPT_CMAKE_ON` and `OPT_CMAKE_OFF`]. `MESON_ARGS`:: For arguments that take `true` or `false`, see crossref:makefiles[options-meson_true, `OPT_MESON_TRUE` and `OPT_MESON_FALSE`]. + For arguments that take `yes` or `no`, use crossref:makefiles[options-meson_yes, `OPT_MESON_YES` and `OPT_MESON_NO`]. + For arguments that take `enabled` or `disabled`, see crossref:makefiles[options-meson_enabled, `OPT_MESON_ENABLED` and `OPT_MESON_DISABLED`]. + For all other cases, use crossref:makefiles[options-meson_on, `OPT_MESON_ON` and `OPT_MESON_OFF`]. `QMAKE_ARGS`:: See crossref:makefiles[options-qmake_on, `OPT_QMAKE_ON` and `OPT_QMAKE_OFF`]. `USE_*`:: See crossref:makefiles[options-use, `OPT_USE` and `OPT_USE_OFF`]. `*_DEPENDS`:: See crossref:makefiles[options-dependencies, Dependencies, `OPT_DEPTYPE` and `OPT_DEPTYPE_OFF`]. `*` (Any variable):: The most used variables have direct helpers, see crossref:makefiles[options-variables, Generic Variables Replacement, `OPT_VARIABLE` and `OPT_VARIABLE_OFF`]. + For any variable without a specific helper, see crossref:makefiles[options-vars, `OPT_VARS` and `OPT_VARS_OFF`]. Options dependencies:: When an option need another option to work, see crossref:makefiles[options-implies, `OPT_IMPLIES`]. Options conflicts:: When an option cannot work if another is also enabled, see crossref:makefiles[options-prevents, `OPT_PREVENTS` and `OPT_PREVENTS_MSG`]. Build targets:: When an option need some extra processing, see crossref:makefiles[options-targets, Additional Build Targets, `_target_-_OPT_-on` and `_target_-_OPT_-off`]. [[options_sub]] ==== `OPTIONS_SUB` If `OPTIONS_SUB` is set to `yes` then each of the options added to `OPTIONS_DEFINE` will be added to `PLIST_SUB` and `SUB_LIST`, for example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPTIONS_SUB= yes .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} PLIST_SUB+= OPT1="" NO_OPT1="@comment " SUB_LIST+= OPT1="" NO_OPT1="@comment " .else PLIST_SUB+= OPT1="@comment " NO_OPT1="" SUB_LIST+= OPT1="@comment " NO_OPT1="" .endif .... [NOTE] ==== The value of `OPTIONS_SUB` is ignored. Setting it to any value will add `PLIST_SUB` and `SUB_LIST` entries for _all_ options. ==== [[options-use]] ==== `OPT_USE` and `OPT_USE_OFF` When option _OPT_ is selected, for each `_key=value_` pair in ``OPT_USE``, _value_ is appended to the corresponding `USE_KEY`. If _value_ has spaces in it, replace them with commas and they will be changed back to spaces during processing. `OPT_USE_OFF` works the same way, but when `OPT` is _not_ selected. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_USES= xorg OPT1_USE= mysql=yes xorg=x11,xextproto,xext,xrandr OPT1_USE_OFF= openssl=yes .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} USE_MYSQL= yes USES+= xorg USE_XORG= x11 xextproto xext xrandr .else USE_OPENSSL= yes .endif .... [[options-configure-helpers]] ==== `CONFIGURE_ARGS` Helpers [[options-configure_enable]] ===== `OPT_CONFIGURE_ENABLE` When option _OPT_ is selected, for each _entry_ in `OPT_CONFIGURE_ENABLE` then `--enable-_entry_` is appended to `CONFIGURE_ARGS`. When option _OPT_ is _not_ selected, `--disable-_entry_` is appended to `CONFIGURE_ARGS`. An optional argument can be specified with an `=` symbol. This argument is only appended to the `--enable-_entry_` configure option. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 OPT1_CONFIGURE_ENABLE= test1 test2 OPT2_CONFIGURE_ENABLE= test2=exhaustive .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} CONFIGURE_ARGS+= --enable-test1 --enable-test2 .else CONFIGURE_ARGS+= --disable-test1 --disable-test2 .endif .if ${PORT_OPTIONS:MOPT2} CONFIGURE_ARGS+= --enable-test2=exhaustive .else CONFIGURE_ARGS+= --disable-test2 .endif .... [[options-configure_with]] ===== `OPT_CONFIGURE_WITH` When option _OPT_ is selected, for each _entry_ in `OPT_CONFIGURE_WITH` then `--with-_entry_` is appended to `CONFIGURE_ARGS`. When option _OPT_ is _not_ selected, `--without-_entry_` is appended to `CONFIGURE_ARGS`. An optional argument can be specified with an `=` symbol. This argument is only appended to the `--with-_entry_` configure option. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 OPT1_CONFIGURE_WITH= test1 OPT2_CONFIGURE_WITH= test2=exhaustive .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 .include .if ${PORT_OPTIONS:MOPT1} CONFIGURE_ARGS+= --with-test1 .else CONFIGURE_ARGS+= --without-test1 .endif .if ${PORT_OPTIONS:MOPT2} CONFIGURE_ARGS+= --with-test2=exhaustive .else CONFIGURE_ARGS+= --without-test2 .endif .... [[options-configure_on]] ===== `OPT_CONFIGURE_ON` and `OPT_CONFIGURE_OFF` When option _OPT_ is selected, the value of `OPT_CONFIGURE_ON`, if defined, is appended to `CONFIGURE_ARGS`. `OPT_CONFIGURE_OFF` works the same way, but when `OPT` is _not_ selected. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_CONFIGURE_ON= --add-test OPT1_CONFIGURE_OFF= --no-test .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} CONFIGURE_ARGS+= --add-test .else CONFIGURE_ARGS+= --no-test .endif .... [TIP] ==== Most of the time, the helpers in crossref:makefiles[options-configure_enable, `OPT_CONFIGURE_ENABLE`] and crossref:makefiles[options-configure_with, `OPT_CONFIGURE_WITH`] provide a shorter and more comprehensive functionality. ==== [[options-cmake-helpers]] ==== `CMAKE_ARGS` Helpers [[options-cmake_on]] ===== `OPT_CMAKE_ON` and `OPT_CMAKE_OFF` When option _OPT_ is selected, the value of `OPT_CMAKE_ON`, if defined, is appended to `CMAKE_ARGS`. `OPT_CMAKE_OFF` works the same way, but when `OPT` is _not_ selected. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_CMAKE_ON= -DTEST:BOOL=true -DDEBUG:BOOL=true OPT1_CMAKE_OFF= -DOPTIMIZE:BOOL=true .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} CMAKE_ARGS+= -DTEST:BOOL=true -DDEBUG:BOOL=true .else CMAKE_ARGS+= -DOPTIMIZE:BOOL=true .endif .... [TIP] ==== See crossref:makefiles[options-cmake_bool, `OPT_CMAKE_BOOL` and `OPT_CMAKE_BOOL_OFF`] for a shorter helper when the value is boolean. ==== [[options-cmake_bool]] ===== `OPT_CMAKE_BOOL` and `OPT_CMAKE_BOOL_OFF` When option _OPT_ is selected, for each _entry_ in `OPT_CMAKE_BOOL` then `-D__entry__:BOOL=true` is appended to `CMAKE_ARGS`. When option _OPT_ is _not_ selected, `-D__entry__:BOOL=false` is appended to `CMAKE_ARGS`. `OPT_CMAKE_BOOL_OFF` is the opposite, `-D__entry__:BOOL=false` is appended to `CMAKE_ARGS` when the option is selected, and `-D__entry__:BOOL=true` when the option is _not_ selected. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_CMAKE_BOOL= TEST DEBUG OPT1_CMAKE_BOOL_OFF= OPTIMIZE .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} CMAKE_ARGS+= -DTEST:BOOL=true -DDEBUG:BOOL=true \ -DOPTIMIZE:BOOL=false .else CMAKE_ARGS+= -DTEST:BOOL=false -DDEBUG:BOOL=false \ -DOPTIMIZE:BOOL=true .endif .... [[options-meson-helpers]] ==== `MESON_ARGS` Helpers [[options-meson_on]] ===== `OPT_MESON_ON` and `OPT_MESON_OFF` When option _OPT_ is selected, the value of `OPT_MESON_ON`, if defined, is appended to `MESON_ARGS`. `OPT_MESON_OFF` works the same way, but when `OPT` is _not_ selected. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_MESON_ON= -Dopt=1 OPT1_MESON_OFF= -Dopt=2 .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} MESON_ARGS+= -Dopt=1 .else MESON_ARGS+= -Dopt=2 .endif .... [[options-meson_true]] ===== `OPT_MESON_TRUE` and `OPT_MESON_FALSE` When option _OPT_ is selected, for each _entry_ in `OPT_MESON_TRUE` then `-D__entry__=true` is appended to `MESON_ARGS`. When option _OPT_ is _not_ selected, `-D__entry__=false` is appended to `MESON_ARGS`. `OPT_MESON_FALSE` is the opposite, `-D__entry__=false` is appended to `MESON_ARGS` when the option is selected, and `-D__entry__=true` when the option is _not_ selected. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_MESON_TRUE= test debug OPT1_MESON_FALSE= optimize .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} MESON_ARGS+= -Dtest=true -Ddebug=true \ -Doptimize=false .else MESON_ARGS+= -Dtest=false -Ddebug=false \ -Doptimize=true .endif .... [[options-meson_yes]] ===== `OPT_MESON_YES` and `OPT_MESON_NO` When option _OPT_ is selected, for each _entry_ in `OPT_MESON_YES` then `-D__entry__=yes` is appended to `MESON_ARGS`. When option _OPT_ is _not_ selected, `-D__entry__=no` is appended to `MESON_ARGS`. `OPT_MESON_NO` is the opposite, `-D__entry__=no` is appended to `MESON_ARGS` when the option is selected, and `-D__entry__=yes` when the option is _not_ selected. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_MESON_YES= test debug OPT1_MESON_NO= optimize .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} MESON_ARGS+= -Dtest=yes -Ddebug=yes \ -Doptimize=no .else MESON_ARGS+= -Dtest=no -Ddebug=no \ -Doptimize=yes .endif .... [[options-meson_enabled]] ===== `OPT_MESON_ENABLED` and `OPT_MESON_DISABLED` When option _OPT_ is selected, for each _entry_ in `OPT_MESON_ENABLED` then `-D__entry__=enabled` is appended to `MESON_ARGS`. When option _OPT_ is _not_ selected, `-D__entry__=disabled` is appended to `MESON_ARGS`. `OPT_MESON_DISABLED` is the opposite, `-D__entry__=disabled` is appended to `MESON_ARGS` when the option is selected, and `-D__entry__=enabled` when the option is _not_ selected. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_MESON_ENABLED= test OPT1_MESON_DISABLED= debug .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} MESON_ARGS+= -Dtest=enabled -Ddebug=disabled .else MESON_ARGS+= -Dtest=disabled -Ddebug=enabled .endif .... [[options-qmake_on]] ==== `OPT_QMAKE_ON` and `OPT_QMAKE_OFF` When option _OPT_ is selected, the value of `OPT_QMAKE_ON`, if defined, is appended to `QMAKE_ARGS`. `OPT_QMAKE_OFF` works the same way, but when `OPT` is _not_ selected. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_QMAKE_ON= -DTEST:BOOL=true OPT1_QMAKE_OFF= -DPRODUCTION:BOOL=true .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} QMAKE_ARGS+= -DTEST:BOOL=true .else QMAKE_ARGS+= -DPRODUCTION:BOOL=true .endif .... [[options-implies]] ==== `OPT_IMPLIES` Provides a way to add dependencies between options. When _OPT_ is selected, all the options listed in this variable will be selected too. Using the crossref:makefiles[options-configure_enable,`OPT_CONFIGURE_ENABLE`] described earlier to illustrate: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 OPT1_IMPLIES= OPT2 OPT1_CONFIGURE_ENABLE= opt1 OPT2_CONFIGURE_ENABLE= opt2 .... Is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 .include .if ${PORT_OPTIONS:MOPT1} CONFIGURE_ARGS+= --enable-opt1 .else CONFIGURE_ARGS+= --disable-opt1 .endif .if ${PORT_OPTIONS:MOPT2} || ${PORT_OPTIONS:MOPT1} CONFIGURE_ARGS+= --enable-opt2 .else CONFIGURE_ARGS+= --disable-opt2 .endif .... [[options-implies-ex1]] .Simple Use of `OPT_IMPLIES` [example] ==== This port has a `X11` option, and a `GNOME` option that needs the `X11` option to be selected to build. [.programlisting] .... OPTIONS_DEFINE= X11 GNOME OPTIONS_DEFAULT= X11 X11_USES= xorg X11_USE= xorg=xi,xextproto GNOME_USE= gnome=gtk30 GNOME_IMPLIES= X11 .... ==== [[options-prevents]] ==== `OPT_PREVENTS` and `OPT_PREVENTS_MSG` Provides a way to add conflicts between options. When _OPT_ is selected, all the options listed in `OPT_PREVENTS` must be un-selected. If `OPT_PREVENTS_MSG` is set and a conflict is triggered, its content will be shown explaining why they conflict. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 OPT1_PREVENTS= OPT2 OPT1_PREVENTS_MSG= OPT1 and OPT2 enable conflicting options .... Is roughly equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 .include .if ${PORT_OPTIONS:MOPT2} && ${PORT_OPTIONS:MOPT1} BROKEN= Option OPT1 conflicts with OPT2 (select only one) .endif .... The only difference is that the first one will write an error after running `make config`, suggesting changing the selected options. [[options-prevents-ex1]] .Simple Use of `OPT_PREVENTS` [example] ==== This port has `X509` and `SCTP` options. Both options add patches, but the patches conflict with each other, so they cannot be selected at the same time. [.programlisting] .... OPTIONS_DEFINE= X509 SCTP SCTP_PATCHFILES= ${PORTNAME}-6.8p1-sctp-2573.patch.gz:-p1 SCTP_CONFIGURE_WITH= sctp X509_PATCH_SITES= http://www.roumenpetrov.info/openssh/x509/:x509 X509_PATCHFILES= ${PORTNAME}-7.0p1+x509-8.5.diff.gz:-p1:x509 X509_PREVENTS= SCTP X509_PREVENTS_MSG= X509 and SCTP patches conflict .... ==== [[options-vars]] ==== `OPT_VARS` and `OPT_VARS_OFF` Provides a generic way to set and append to variables. [WARNING] ==== Before using `OPT_VARS` and `OPT_VARS_OFF`, see if there is already a more specific helper available in crossref:makefiles[options-variables, Generic Variables Replacement, `OPT_VARIABLE` and `OPT_VARIABLE_OFF`]. ==== When option _OPT_ is selected, and `OPT_VARS` defined, `_key_=_value_` and `_key_+=_value_` pairs are evaluated from `OPT_VARS`. An `=` cause the existing value of `KEY` to be overwritten, an `+=` appends to the value. `OPT_VARS_OFF` works the same way, but when `OPT` is _not_ selected. [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 OPT3 OPT1_VARS= also_build+=bin1 OPT2_VARS= also_build+=bin2 OPT3_VARS= bin3_build=yes OPT3_VARS_OFF= bin3_build=no MAKE_ARGS= ALSO_BUILD="${ALSO_BUILD}" BIN3_BUILD="${BIN3_BUILD}" .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 OPT3 MAKE_ARGS= ALSO_BUILD="${ALSO_BUILD}" BIN3_BUILD="${BIN3_BUILD}" .include .if ${PORT_OPTIONS:MOPT1} ALSO_BUILD+= bin1 .endif .if ${PORT_OPTIONS:MOPT2} ALSO_BUILD+= bin2 .endif .if ${PORT_OPTIONS:MOPT3} BIN3_BUILD= yes .else BIN3_BUILD= no .endif .... [IMPORTANT] ==== Values containing whitespace must be enclosed in quotes: [.programlisting] .... OPT_VARS= foo="bar baz" .... This is due to the way man:make[1] variable expansion deals with whitespace. When `OPT_VARS= foo=bar baz` is expanded, the variable ends up containing two strings, `foo=bar` and `baz`. But the submitter probably intended there to be only one string, `foo=bar baz`. Quoting the value prevents whitespace from being used as a delimiter. Also, _do not_ add extra spaces after the `_var_=` sign and before the value, it would also be split into two strings. _This will not work_: [.programlisting] .... OPT_VARS= foo= bar .... ==== [[options-dependencies]] ==== Dependencies, `OPT_DEPTYPE` and `OPT_DEPTYPE_OFF` For any of these dependency types: * `PKG_DEPENDS` * `EXTRACT_DEPENDS` * `PATCH_DEPENDS` * `FETCH_DEPENDS` * `BUILD_DEPENDS` * `LIB_DEPENDS` * `RUN_DEPENDS` When option _OPT_ is selected, the value of `OPT_DEPTYPE`, if defined, is appended to `DEPTYPE`. `OPT_DEPTYPE_OFF` works the same, but when `OPT` is _not_ selected. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_LIB_DEPENDS= liba.so:devel/a OPT1_LIB_DEPENDS_OFF= libb.so:devel/b .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} LIB_DEPENDS+= liba.so:devel/a .else LIB_DEPENDS+= libb.so:devel/b .endif .... [[options-variables]] ==== Generic Variables Replacement, `OPT_VARIABLE` and `OPT_VARIABLE_OFF` For any of these variables: * `ALL_TARGET` * `BINARY_ALIAS` * `BROKEN` * `CATEGORIES` * `CFLAGS` * `CONFIGURE_ENV` * `CONFLICTS` * `CONFLICTS_BUILD` * `CONFLICTS_INSTALL` * `CPPFLAGS` * `CXXFLAGS` * `DESKTOP_ENTRIES` * `DISTFILES` * `EXTRACT_ONLY` * `EXTRA_PATCHES` * `GH_ACCOUNT` * `GH_PROJECT` * `GH_SUBDIR` * `GH_TAGNAME` * `GH_TUPLE` * `GL_ACCOUNT` * `GL_COMMIT` * `GL_PROJECT` * `GL_SITE` * `GL_SUBDIR` * `GL_TUPLE` * `IGNORE` * `INFO` * `INSTALL_TARGET` * `LDFLAGS` * `LIBS` * `MAKE_ARGS` * `MAKE_ENV` * `MASTER_SITES` * `PATCHFILES` * `PATCH_SITES` * `PLIST_DIRS` * `PLIST_FILES` * `PLIST_SUB` * `PORTDOCS` * `PORTEXAMPLES` * `SUB_FILES` * `SUB_LIST` * `TEST_TARGET` * `USES` When option _OPT_ is selected, the value of `OPT_ABOVEVARIABLE`, if defined, is appended to `_ABOVEVARIABLE_`. `OPT_ABOVEVARIABLE_OFF` works the same way, but when `OPT` is _not_ selected. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_USES= gmake OPT1_CFLAGS_OFF= -DTEST .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} USES+= gmake .else CFLAGS+= -DTEST .endif .... [NOTE] ==== Some variables are not in this list, in particular `PKGNAMEPREFIX` and `PKGNAMESUFFIX`. This is intentional. A port _must not_ change its name when its option set changes. ==== [WARNING] ==== Some of these variables, at least `ALL_TARGET`, `DISTFILES` and `INSTALL_TARGET`, have their default values set _after_ the options are processed. With these lines in the [.filename]#Makefile#: [.programlisting] .... ALL_TARGET= all DOCS_ALL_TARGET= doc .... If the `DOCS` option is enabled, `ALL_TARGET` will have a final value of `all doc`; if the option is disabled, it would have a value of `all`. With only the options helper line in the [.filename]#Makefile#: [.programlisting] .... DOCS_ALL_TARGET= doc .... If the `DOCS` option is enabled, `ALL_TARGET` will have a final value of `doc`; if the option is disabled, it would have a value of `all`. ==== [[options-targets]] ==== Additional Build Targets, `_target_-_OPT_-on` and `_target_-_OPT_-off` These [.filename]#Makefile# targets can accept optional extra build targets: * `pre-fetch` * `do-fetch` * `post-fetch` * `pre-extract` * `do-extract` * `post-extract` * `pre-patch` * `do-patch` * `post-patch` * `pre-configure` * `do-configure` * `post-configure` * `pre-build` * `do-build` * `post-build` * `pre-install` * `do-install` * `post-install` * `post-stage` * `pre-package` * `do-package` * `post-package` When option _OPT_ is selected, the target `_TARGET_-_OPT_-on`, if defined, is executed after `_TARGET_`. `_TARGET_-_OPT_-off` works the same way, but when `OPT` is _not_ selected. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 post-patch-OPT1-on: @${REINPLACE_CMD} -e '/opt1/s|/usr/bin/|${EXAMPLESDIR}/|' ${WRKSRC}/Makefile post-patch-OPT1-off: @${REINPLACE_CMD} -e '/opt1/s|/usr/bin/|${PREFIX}/bin/|' ${WRKSRC}/Makefile .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include post-patch: .if ${PORT_OPTIONS:MOPT1} @${REINPLACE_CMD} -e '/opt1/s|/usr/bin/|${EXAMPLESDIR}/|' ${WRKSRC}/Makefile .else @${REINPLACE_CMD} -e '/opt1/s|/usr/bin/|${PREFIX}/bin/|' ${WRKSRC}/Makefile .endif .... [[makefile-wrkdir]] == Specifying the Working Directory Each port is extracted into a working directory, which must be writable. The ports system defaults to having `DISTFILES` unpack in to a directory called `${DISTNAME}`. In other words, if the [.filename]#Makefile# has: [.programlisting] .... PORTNAME= foo DISTVERSION= 1.0 .... then the port's distribution files contain a top-level directory, [.filename]#foo-1.0#, and the rest of the files are located under that directory. A number of variables can be overridden if that is not the case. [[makefile-wrksrc]] === `WRKSRC` The variable lists the name of the directory that is created when the application's distfiles are extracted. If our previous example extracted into a directory called [.filename]#foo# (and not [.filename]#foo-1.0#) write: [.programlisting] .... WRKSRC= ${WRKDIR}/foo .... or possibly [.programlisting] .... WRKSRC= ${WRKDIR}/${PORTNAME} .... [[makefile-wrksrc_subdir]] === `WRKSRC_SUBDIR` If the source files needed for the port are in a subdirectory of the extracted distribution file, set `WRKSRC_SUBDIR` to that directory. [.programlisting] .... WRKSRC_SUBDIR= src .... [[makefile-no_wrksubdir]] === `NO_WRKSUBDIR` If the port does not extract in to a subdirectory at all, then set `NO_WRKSUBDIR` to indicate that. [.programlisting] .... NO_WRKSUBDIR= yes .... [NOTE] ==== Because `WRKDIR` is the only directory that is supposed to be writable during the build, and is used to store many files recording the status of the build, the port's extraction will be forced into a subdirectory. ==== [[conflicts]] == Conflict Handling There are three different variables to register a conflict between packages and ports: `CONFLICTS`, `CONFLICTS_INSTALL` and `CONFLICTS_BUILD`. [NOTE] ==== The conflict variables automatically set the variable `IGNORE`, which is more fully documented in crossref:porting-dads[dads-noinstall,Marking a Port Not Installable with `BROKEN`, `FORBIDDEN`, or `IGNORE`]. ==== When removing one of several conflicting ports, it is advisable to retain `CONFLICTS` in those other ports for a few months to cater for users who only update once in a while. [[conclicts-conflicts_install]] `CONFLICTS_INSTALL`:: If the package cannot coexist with other packages (because of file conflicts, runtime incompatibilities, etc.). `CONFLICTS_INSTALL` check is done after the build stage and prior to the install stage. [[conclicts-conflicts_build]] `CONFLICTS_BUILD`:: If the port cannot be built when other specific ports are already installed. Build conflicts are not recorded in the resulting package. [[conclicts-conflicts]] `CONFLICTS`:: If the port cannot be built if a certain port is already installed and the resulting package cannot coexist with the other package. `CONFLICTS` check is done prior to the build stage and prior to the install stage. Each space-separated item in the `CONFLICTS*` variable values is matched against packages except the one being built, using shell globbing rules. This allows listing all flavors of a port in a conflict list instead of having to take pains to exclude the flavor being built from that list. For example, if git-lite is installed, `CONFLICTS_INSTALL=git git-lite` would allow to perform: [source,shell] .... % make -C devel/git FLAVOR=lite all deinstall install .... But the following command would report a conflict, since the package base name installed is `git-lite`, while `git` would be built, but cannot be installed in addition to `git-lite`: [source,shell] .... % make -C devel/git FLAVOR=default all deinstall install .... Without that feature, the Makefile would need one `_flavor__CONFLICTS_INSTALL` for each flavor, listing every other flavor. The most common content of one of these variable is the package base of another port. The package base is the package name without the appended version, it can be obtained by running `make -V PKGBASE`. [[conflicts-ex1]] .Basic usage of `CONFLICTS*` [example] ==== package:dns/bind99[] cannot be installed if package:dns/bind910[] is present because they install same files. First gather the package base to use: [source,shell] .... % make -C dns/bind99 -V PKGBASE bind99 % make -C dns/bind910 -V PKGBASE bind910 .... Then add to the [.filename]#Makefile# of package:dns/bind99[]: [.programlisting] .... CONFLICTS_INSTALL= bind910 .... And add to the [.filename]#Makefile# of package:dns/bind910[]: [.programlisting] .... CONFLICTS_INSTALL= bind99 .... ==== Sometimes, only certain versions of another port are incompatible. When this is the case, use the full package name including the version. If necessary, use shell globs like `*` and `?` so that all necessary versions are matched. [[conflicts-ex2]] .Using `CONFLICTS*` With Globs. [example] ==== From versions from 2.0 and up-to 2.4.1_2, package:deskutils/gnotime[] used to install a bundled version of package:databases/qof[]. To reflect this past, the [.filename]#Makefile# of package:databases/qof[] contains: [.programlisting] .... CONFLICTS_INSTALL= gnotime-2.[0-3]* \ gnotime-2.4.0* gnotime-2.4.1 \ gnotime-2.4.1_[12] .... The first entry match versions `2.0` through `2.3`, the second all the revisions of `2.4.0`, the third the exact `2.4.1` version, and the last the first and second revisions of the `2.4.1` version. package:deskutils/gnotime[] does not have any conflicts line because its current version does not conflict with anything else. ==== The variable `DISABLE_CONFLICTS` may be temporarily set when making targets that are not affected by conflicts. The variable is not to be set in port Makefiles. [source,shell] .... % make -DDISABLE_CONFLICTS patch .... [[install]] == Installing Files [IMPORTANT] ==== The `install` phase is very important to the end user because it adds files to their system. All the additional commands run in the port [.filename]#Makefile#'s `*-install` targets should be echoed to the screen. _Do not_ silence these commands with `@` or `.SILENT`. ==== [[install-macros]] === `INSTALL_*` Macros Use the macros provided in [.filename]#bsd.port.mk# to ensure correct modes of files in the port's `*-install` targets. Set ownership directly in [.filename]#pkg-plist# with the corresponding entries, such as `@(_owner_,_group_,)`, `@owner _owner_`, and `@group _group_`. These operators work until overridden, or until the end of [.filename]#pkg-plist#, so remember to reset them after they are no longer needed. The default ownership is `root:wheel`. See crossref:plist[plist-keywords-base,Base Keywords] for more information. * `INSTALL_PROGRAM` is a command to install binary executables. * `INSTALL_SCRIPT` is a command to install executable scripts. * `INSTALL_LIB` is a command to install shared libraries (but not static libraries). * `INSTALL_KLD` is a command to install kernel loadable modules. Some architectures do not like having the modules stripped, so use this command instead of `INSTALL_PROGRAM`. * `INSTALL_DATA` is a command to install sharable data, including static libraries. * `INSTALL_MAN` is a command to install manpages and other documentation (it does not compress anything). These variables are set to the man:install[1] command with the appropriate flags for each situation. [IMPORTANT] ==== Do not use `INSTALL_LIB` to install static libraries, because stripping them renders them useless. Use `INSTALL_DATA` instead. ==== [[install-strip]] === Stripping Binaries and Shared Libraries Installed binaries should be stripped. Do not strip binaries manually unless absolutely required. The `INSTALL_PROGRAM` macro installs and strips a binary at the same time. The `INSTALL_LIB` macro does the same thing to shared libraries. When a file must be stripped, but neither `INSTALL_PROGRAM` nor `INSTALL_LIB` macros are desirable, `${STRIP_CMD}` strips the program or shared library. This is typically done within the `post-install` target. For example: [.programlisting] .... post-install: ${STRIP_CMD} ${STAGEDIR}${PREFIX}/bin/xdl .... When multiple files need to be stripped: [.programlisting] .... post-install: .for l in geometry media body track world ${STRIP_CMD} ${STAGEDIR}${PREFIX}/lib/lib${PORTNAME}-${l}.so.0 .endfor .... Use man:file[1] on a file to determine if it has been stripped. Binaries are reported by man:file[1] as `stripped`, or `not stripped`. Additionally, man:strip[1] will detect programs that have already been stripped and exit cleanly. [IMPORTANT] ==== When `WITH_DEBUG` is defined, elf files _must not_ be stripped. The variables (`STRIP_CMD`, `INSTALL_PROGRAM`, `INSTALL_LIB`, ...) and crossref:uses[uses,`USES`] provided by the framework handle this automatically. Some software, add `-s` to their `LDFLAGS`, in this case, either remove `-s` if `WITH_DEBUG` is set, or remove it unconditionally and use `STRIP_CMD` in `post-install`. ==== [[install-copytree]] === Installing a Whole Tree of Files Sometimes, a large number of files must be installed while preserving their hierarchical organization. For example, copying over a whole directory tree from `WRKSRC` to a target directory under `PREFIX`. Note that `PREFIX`, `EXAMPLESDIR`, `DATADIR`, and other path variables must always be prepended with `STAGEDIR` to respect staging (see crossref:special[staging,Staging]). Two macros exist for this situation. The advantage of using these macros instead of `cp` is that they guarantee proper file ownership and permissions on target files. The first macro, `COPYTREE_BIN`, will set all the installed files to be executable, thus being suitable for installing into [.filename]#PREFIX/bin#. The second macro, `COPYTREE_SHARE`, does not set executable permissions on files, and is therefore suitable for installing files under [.filename]#PREFIX/share# target. [.programlisting] .... post-install: ${MKDIR} ${STAGEDIR}${EXAMPLESDIR} (cd ${WRKSRC}/examples && ${COPYTREE_SHARE} . ${STAGEDIR}${EXAMPLESDIR}) .... This example will install the contents of the [.filename]#examples# directory in the vendor distfile to the proper examples location of the port. [.programlisting] .... post-install: ${MKDIR} ${STAGEDIR}${DATADIR}/summer (cd ${WRKSRC}/temperatures && ${COPYTREE_SHARE} "June July August" ${STAGEDIR}${DATADIR}/summer) .... And this example will install the data of summer months to the [.filename]#summer# subdirectory of a [.filename]#DATADIR#. Additional `find` arguments can be passed via the third argument to `COPYTREE_*` macros. For example, to install all files from the first example except Makefiles, one can use these commands. [.programlisting] .... post-install: ${MKDIR} ${STAGEDIR}${EXAMPLESDIR} (cd ${WRKSRC}/examples && \ ${COPYTREE_SHARE} . ${STAGEDIR}${EXAMPLESDIR} "! -name Makefile") .... These macros do not add the installed files to [.filename]#pkg-plist#. They must be added manually. For optional documentation (`PORTDOCS`, see crossref:makefiles[install-documentation, Install Additional Documentation]) and examples (`PORTEXAMPLES`), the `%%PORTDOCS%%` or `%%PORTEXAMPLES%%` prefixes must be prepended in [.filename]#pkg-plist#. [[install-documentation]] === Install Additional Documentation If the software has some documentation other than the standard man and info pages that is useful for the user, install it under `DOCSDIR`. This can be done, like the previous item, in the `post-install` target. Create a new directory for the port. The directory name is `DOCSDIR`. This usually equals `PORTNAME`. However, if the user might want different versions of the port to be installed at the same time, the whole `PKGNAME` can be used. Since only the files listed in [.filename]#pkg-plist# are installed, it is safe to always install documentation to `STAGEDIR` (see crossref:special[staging,Staging]). Hence `.if` blocks are only needed when the installed files are large enough to cause significant I/O overhead. [.programlisting] .... post-install: ${MKDIR} ${STAGEDIR}${DOCSDIR} ${INSTALL_DATA} ${WRKSRC}/docs/xvdocs.ps ${STAGEDIR}${DOCSDIR} .... On the other hand, if there is a DOCS option in the port, install the documentation in a `post-install-DOCS-on` target. These targets are described in crossref:makefiles[options-targets, Additional Build Targets, `_target_-_OPT_-on` and `_target_-_OPT_-off`]. Here are some handy variables and how they are expanded by default when used in the [.filename]#Makefile#: * `DATADIR` gets expanded to [.filename]#PREFIX/share/PORTNAME#. * `DATADIR_REL` gets expanded to [.filename]#share/PORTNAME#. * `DOCSDIR` gets expanded to [.filename]#PREFIX/share/doc/PORTNAME#. * `DOCSDIR_REL` gets expanded to [.filename]#share/doc/PORTNAME#. * `EXAMPLESDIR` gets expanded to [.filename]#PREFIX/share/examples/PORTNAME#. * `EXAMPLESDIR_REL` gets expanded to [.filename]#share/examples/PORTNAME#. [NOTE] ==== The `DOCS` option only controls additional documentation installed in `DOCSDIR`. It does not apply to standard man pages and info pages. Things installed in `EXAMPLESDIR` are controlled by the `EXAMPLES` option. ==== These variables are exported to `PLIST_SUB`. Their values will appear there as pathnames relative to [.filename]#PREFIX# if possible. That is, [.filename]#share/doc/PORTNAME# will be substituted for `%%DOCSDIR%%` in the packing list by default, and so on. (See more on [.filename]#pkg-plist# substitution crossref:plist[plist-sub,here].) All conditionally installed documentation files and directories are included in [.filename]#pkg-plist# with the `%%PORTDOCS%%` prefix, for example: [.programlisting] .... %%PORTDOCS%%%%DOCSDIR%%/AUTHORS %%PORTDOCS%%%%DOCSDIR%%/CONTACT .... As an alternative to enumerating the documentation files in [.filename]#pkg-plist#, a port can set the variable `PORTDOCS` to a list of file names and shell glob patterns to add to the final packing list. The names will be relative to `DOCSDIR`. Therefore, a port that utilizes `PORTDOCS`, and uses a non-default location for its documentation, must set `DOCSDIR` accordingly. If a directory is listed in `PORTDOCS` or matched by a glob pattern from this variable, the entire subtree of contained files and directories will be registered in the final packing list. If the `DOCS` option has been unset then files and directories listed in `PORTDOCS` would not be installed or added to port packing list. Installing the documentation at `PORTDOCS` as shown above remains up to the port itself. A typical example of utilizing `PORTDOCS`: [.programlisting] .... PORTDOCS= README.* ChangeLog docs/* .... [NOTE] ==== The equivalents of `PORTDOCS` for files installed under `DATADIR` and `EXAMPLESDIR` are `PORTDATA` and `PORTEXAMPLES`, respectively. The contents of [.filename]#pkg-message# are displayed upon installation. See crossref:pkg-files[porting-message,the section on using [.filename]#pkg-message#] for details. [.filename]#pkg-message# does not need to be added to [.filename]#pkg-plist#. ==== [[install-subdirs]] === Subdirectories Under `PREFIX` Try to let the port put things in the right subdirectories of `PREFIX`. Some ports lump everything and put it in the subdirectory with the port's name, which is incorrect. Also, many ports put everything except binaries, header files and manual pages in a subdirectory of [.filename]#lib#, which does not work well with the BSD paradigm. Many of the files must be moved to one of these directories: [.filename]#etc# (setup/configuration files), [.filename]#libexec# (executables started internally), [.filename]#sbin# (executables for superusers/managers), [.filename]#info# (documentation for info browser) or [.filename]#share# (architecture independent files). See man:hier[7] for details; the rules governing [.filename]#/usr# pretty much apply to [.filename]#/usr/local# too. The exception are ports dealing with USENET "news". They may use [.filename]#PREFIX/news# as a destination for their files. [[binary-alias]] == Use `BINARY_ALIAS` to Rename Commands Instead of Patching the Build When `BINARY_ALIAS` is defined it will create symlinks of the given commands in a directory which will be prepended to `PATH`. Use it to substitute hardcoded commands the build phase relies on without having to patch any build files. [[binary-alias-ex1]] .Using `BINARY_ALIAS` to Make `gsed` Available as `sed` [example] ==== Some ports expect `sed` to behave like GNU sed and use features that man:sed[1] does not provide. GNU sed is available from package:textproc/gsed[] on FreeBSD. Use `BINARY_ALIAS` to substitute `sed` with `gsed` for the duration of the build: [.programlisting] .... BUILD_DEPENDS= gsed:textproc/gsed ... BINARY_ALIAS= sed=gsed .... ==== [[binary-alias-ex2]] .Using `BINARY_ALIAS` to Provide Aliases for Hardcoded `python3` Commands [example] ==== A port that has a hardcoded reference to `python3` in its build scripts will need to have it available in `PATH` at build time. Use `BINARY_ALIAS` to create an alias that points to the right Python 3 binary: [.programlisting] .... USES= python:3.4+,build ... BINARY_ALIAS= python3=${PYTHON_CMD} .... See crossref:special[using-python,Using Python] for more information about `USES=python`. ==== [NOTE] ==== Binary aliases are created after the dependencies provided via `BUILD_DEPENDS` and `LIB_DEPENDS` are processed and before the `configure` target. This leads to various limitations. For example, programs installed via `TEST_DEPENDS` cannot be used to create a binary alias as test dependencies specified this way are processed after binary aliases are created. ==== diff --git a/documentation/content/en/books/porters-handbook/pkg-files/_index.adoc b/documentation/content/en/books/porters-handbook/pkg-files/_index.adoc index d4530108c9..ae5432e2ac 100644 --- a/documentation/content/en/books/porters-handbook/pkg-files/_index.adoc +++ b/documentation/content/en/books/porters-handbook/pkg-files/_index.adoc @@ -1,357 +1,357 @@ --- title: Chapter 9. pkg-* prev: books/porters-handbook/plist next: books/porters-handbook/testing description: Tricks about the pkg-* files tags: ["pkg", "pkg-message", "UCL", "pkg-install", "pkg-deinstall"] showBookMenu: true weight: 9 params: path: "/books/porters-handbook/pkg-files/" --- [[pkg-files]] = pkg-* :doctype: book :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumoffset: 9 :partnums: :source-highlighter: rouge :experimental: :images-path: books/porters-handbook/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] There are some tricks we have not mentioned yet about the [.filename]#pkg-*# files that come in handy sometimes. [[porting-message]] == pkg-message To display a message when the package is installed, place the message in [.filename]#pkg-message#. This capability is often useful to display additional installation steps to be taken after a `pkg install` or `pkg upgrade`. [IMPORTANT] ==== * [.filename]#pkg-message# must contain only information that is _vital_ to setup and operation on FreeBSD, and that is unique to the port in question. * Setup information should only be shown on initial install. Upgrade instructions should be shown only when upgrading from the relevant version. * Do not surround the messages with either whitespace or lines of symbols (like `----------`, `**********`, or `==========`). Leave the formatting to man:pkg[8]. * Committers have blanket approval to constrain existing messages to install or upgrade ranges using the UCL format specifications. * Please be sure to refer to the proper tools for handling services. ** Use `service name start` to start a service rather than using `/usr/local/etc/rc.d/name start` ** Use `sysrc name_enable=YES` to change options in rc.conf ==== pkg-message supports two formats: raw:: A regular plain text file. Its message is only displayed on install. UCL:: If the file starts with "`[`" then it is considered to be a UCL file. The UCL format is described on https://github.com/vstakhov/libucl[libucl's GitHub page]. [NOTE] ==== Do not add an entry for [.filename]#pkg-message# in [.filename]#pkg-plist#. ==== [[porting-message-ucl]] === UCL in pkg-message The format is the following. It should be an array of objects. The objects themselves can have these keywords: `message`:: The actual message to be displayed. This keyword is mandatory. `type`:: When the message should be displayed. `maximum_version`:: Only if `type` is `upgrade`. Display if upgrading from a version strictly lower than the version specified. `minimum_version`:: Only if `type` is `upgrade`. Display if upgrading from a version strictly greater than the version specified. The `maximum_version` and `minimum_version` keywords can be combined. The `type` keyword can have three values: `install`:: The message should only be displayed when the package is installed. `remove`:: The message should only be displayed when the package is removed. `upgrade`:: the message should only be displayed during an upgrade of the package.. [IMPORTANT] ==== To preserve the compatibility with non UCL [.filename]#pkg-message# files, the first line of a UCL [.filename]#pkg-message# _MUST be_ a single "`[`", and the last line _MUST be_ a single "`]`". ==== [[porting-message-ucl-short-ex]] .UCL Short Strings [example] ==== The message is delimited by double quotes `"`, this is used for simple single line strings: [.programlisting] .... [ { type: install message: "Simple message" } ] .... ==== [[porting-message-ucl-multiline-ex]] .UCL Multiline Strings [example] ==== Multiline strings use the standard here document notation. The multiline delimiter _must_ start just after `<<` symbols without any whitespace and it _must_ consist of capital letters only. To finish a multiline string, add the delimiter string on a line of its own without any whitespace. The message from crossref:pkg-files[porting-message-ucl-short-ex,UCL Short Strings] can be written as: [.programlisting] .... [ { type: install message: < 1.0 and < 3.0 remove that file." } ] .... [IMPORTANT] -**** +===== When displaying a message on upgrade, it is important to limit when it is being shown to the user. Most of the time it is by using `maximum_version` to limit its usage to upgrades from before a certain version when something specific needs to be done. -**** +===== ==== [[pkg-install]] == pkg-install, pkg-pre-install, and pkg-post-install If the port needs to execute commands when the binary package is installed with `pkg add` or `pkg install`, use [.filename]#pkg-install#. It is run twice by `pkg`, the first time as `${SH} pkg-install ${PKGNAME} PRE-INSTALL` before the package is installed, and the second time as `${SH} pkg-install ${PKGNAME} POST-INSTALL` after it has been installed. `$2` can be tested to determine which mode the script is being run in. The `PKG_PREFIX` environment variable is set to the package installation directory. If using [.filename]#pkg-pre-install# or [.filename]#pkg-post-install# instead, the script is run only once (before or after installing the package), with the single argument `${PKGNAME}`. Using [.filename]#pkg-pre-install.lua# or [.filename]#pkg-post-install.lua# will run a lua script instead of a shell script. Lua scripts run by `pkg` provide some extensions and a few restrictions, both explained in man:pkg-lua-script[5]. [NOTE] ==== Using [.filename]#pkg-pre-install# (or [.filename]#pkg-pre-install.lua#) and [.filename]#pkg-post-install# (or [.filename]#pkg-post-install.lua#) is preferred to using [.filename]#pkg-install#. ==== These scripts are automatically added to the packing list. [IMPORTANT] ==== These scripts are here to simplify package configuration after installation. They _must not_ be abused to start services, stop services, or run any other commands that will modify the currently running system. ==== [[pkg-deinstall]] == pkg-deinstall, pkg-pre-deinstall, and pkg-post-deinstall These scripts execute when a package is removed. The [.filename]#pkg-deinstall# script is run twice by `pkg delete`. The first time as `${SH} pkg-deinstall ${PKGNAME} DEINSTALL` before the port is de-installed and the second time as `${SH} pkg-deinstall ${PKGNAME} POST-DEINSTALL` after the port has been de-installed. `$2` can be tested to determine which mode the script is being run in. The `PKG_PREFIX` environment variable is set to the package installation directory. If using [.filename]#pkg-pre-deinstall# or [.filename]#pkg-post-deinstall# instead, the script is run only once (before or after deinstalling the package), with the single argument `${PKGNAME}`. Using [.filename]#pkg-pre-deinstall.lua# or [.filename]#pkg-post-deinstall.lua# will run a lua script instead of a shell script. Lua scripts run by `pkg` provide some extensions and a few restrictions, both explained in man:pkg-lua-script[5]. [NOTE] ==== Using [.filename]#pkg-pre-deinstall# (or [.filename]#pkg-pre-deinstall.lua#) and [.filename]#pkg-post-deinstall# (or [.filename]#pkg-post-deinstall.lua#) is preferred to using [.filename]#pkg-deinstall#. ==== These scripts are automatically added to the packing list. [IMPORTANT] ==== These scripts are here to simplify cleanup after package deinstallation. They _must not_ be abused to start services, stop services, or run any other commands that will modify the currently running system. ==== [[pkg-names]] == Changing the Names of pkg-* All the names of [.filename]#pkg-\*# are defined using variables that can be changed in the [.filename]#Makefile# if needed. This is especially useful when sharing the same [.filename]#pkg-*# files among several ports or when it is necessary to write to one of these files. See crossref:porting-dads[porting-wrkdir,writing to places other than `WRKDIR`] for why it is a bad idea to write directly into the directory containing the [.filename]#pkg-*# files. Here is a list of variable names and their default values. (`PKGDIR` defaults to `${MASTERDIR}`.) [.informaltable] [cols="1,1", frame="none", options="header"] |=== | Variable | Default value |`DESCR` |`${PKGDIR}/pkg-descr` |`PLIST` |`${PKGDIR}/pkg-plist` |`PKGINSTALL` |`${PKGDIR}/pkg-install` |`PKGPREINSTALL` |`${PKGDIR}/pkg-pre-install` |`PKGPOSTINSTALL` |`${PKGDIR}/pkg-post-install` |`PKGDEINSTALL` |`${PKGDIR}/pkg-deinstall` |`PKGPREDEINSTALL` |`${PKGDIR}/pkg-pre-deinstall` |`PKGPOSTDEINSTALL` |`${PKGDIR}/pkg-post-deinstall` |`PKGMESSAGE` |`${PKGDIR}/pkg-message` |=== [[using-sub-files]] == Making Use of `SUB_FILES` and `SUB_LIST` `SUB_FILES` and `SUB_LIST` are useful for dynamic values in port files, such as the installation `PREFIX` in [.filename]#pkg-message#. `SUB_FILES` specifies a list of files to be automatically modified. Each [.filename]#file# in the `SUB_FILES` list must have a corresponding [.filename]#file.in# present in `FILESDIR`. A modified version will be created as [.filename]#${WRKDIR}/file#. Files defined as a value of `USE_RC_SUBR` are automatically added to `SUB_FILES`. For the files [.filename]#pkg-message#, [.filename]#pkg-install#, and [.filename]#pkg-deinstall#, the corresponding Makefile variable is automatically set to point to the processed version. `SUB_LIST` is a list of `VAR=VALUE` pairs. For each pair, `%%VAR%%` will be replaced with `VALUE` in each file listed in `SUB_FILES`. Several common pairs are automatically defined: `PREFIX`, `LOCALBASE`, `DATADIR`, `DOCSDIR`, `EXAMPLESDIR`, `WWWDIR`, and `ETCDIR`. Any line beginning with `@comment` followed by a space, will be deleted from resulting files after a variable substitution. This example replaces `%%ARCH%%` with the system architecture in a [.filename]#pkg-message#: [.programlisting] .... SUB_FILES= pkg-message SUB_LIST= ARCH=${ARCH} .... Note that for this example, [.filename]#pkg-message.in# must exist in `FILESDIR`. Example of a good [.filename]#pkg-message.in#: [.programlisting] .... Now it is time to configure this package. Copy %%PREFIX%%/shared/examples/putsy/%%ARCH%%.conf into your home directory as .putsy.conf and edit it. .... diff --git a/documentation/content/en/books/porters-handbook/quick-porting/_index.adoc b/documentation/content/en/books/porters-handbook/quick-porting/_index.adoc index 9c25913c38..a3d7547179 100644 --- a/documentation/content/en/books/porters-handbook/quick-porting/_index.adoc +++ b/documentation/content/en/books/porters-handbook/quick-porting/_index.adoc @@ -1,317 +1,317 @@ --- title: Chapter 3. Quick Porting prev: books/porters-handbook/new-port next: books/porters-handbook/slow-porting description: How to quickly create a new FreeBSD Port tags: ["quick porting", "guide", "port", "ports collection", "how-to"] showBookMenu: true weight: 3 params: path: "/books/porters-handbook/quick-porting/" --- [[quick-porting]] = Quick Porting :doctype: book :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumoffset: 3 :partnums: :source-highlighter: rouge :experimental: :images-path: books/porters-handbook/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] This section describes how to quickly create a new port. For applications where this quick method is not adequate, the full "Slow Porting" process is described in crossref:slow-porting[slow-porting,Slow Porting]. First, get the original tarball and put it into `DISTDIR`, which defaults to [.filename]#/usr/ports/distfiles#. [NOTE] ==== These steps assume that the software compiled out-of-the-box. In other words, absolutely no changes were required for the application to work on a FreeBSD system. If anything had to be changed, refer to crossref:slow-porting[slow-porting,Slow Porting]. ==== [NOTE] ==== It is recommended to set the `DEVELOPER` man:make[1] variable in [.filename]#/etc/make.conf# before getting into porting. [source,shell] .... # echo DEVELOPER=yes >> /etc/make.conf .... This setting enables the "developer mode" that displays deprecation warnings and activates some further quality checks on calling `make`. ==== [[porting-makefile]] == Writing the Makefile The minimal [.filename]#Makefile# would look something like this: [.programlisting] .... PORTNAME= oneko DISTVERSION= 1.1b CATEGORIES= games MASTER_SITES= ftp://ftp.rediris.es/sites/ftp.freebsd.org/pub/FreeBSD/ MAINTAINER= youremail@example.com COMMENT= Cat chasing a mouse all over the screen WWW= http://www.daidouji.com/oneko/ .include .... Try to figure it out. A more detailed example is shown in the crossref:porting-samplem[porting-samplem,sample Makefile] section. [[porting-desc]] == Writing the Description Files There are two description files that are required for any port, whether they actually package or not. They are [.filename]#pkg-descr# and [.filename]#pkg-plist#. Their [.filename]#pkg-# prefix distinguishes them from other files. [[porting-pkg-descr]] === [.filename]#pkg-descr# This is a longer description of the port. One to a few paragraphs concisely explaining what the port does is sufficient. [NOTE] ==== This is _not_ a manual or an in-depth description on how to use or compile the port! _Please be careful when copying from the [.filename]#README# or manpage_. Too often they are not a concise description of the port or are in an awkward format. For example, manpages have justified spacing, which looks particularly bad with monospaced fonts. On the other hand, the content of [.filename]#pkg-descr# must be longer than the crossref:makefiles[makefile-comment,`COMMENT` line from the Makefile]. It must explain in more depth what the port is all about. ==== A well-written [.filename]#pkg-descr# describes the port completely enough that users would not have to consult the documentation or visit the website to understand what the software does, how it can be useful, or what particularly nice features it has. Mentioning certain requirements like a graphical toolkit, heavy dependencies, runtime environment, or implementation languages help users decide whether this port will work for them. [NOTE] ==== The URL that used to be included as the last line of the [.filename]#pkg-descr# file has been moved to the [.filename]#Makefile#. ==== [[porting-pkg-plist]] === [.filename]#pkg-plist# This file lists all the files installed by the port. It is also called the "packing list" because the package is generated by packing the files listed here. The pathnames are relative to the installation prefix (usually [.filename]#/usr/local#). Here is a small example: [.programlisting] .... bin/oneko man/man1/oneko.1.gz lib/X11/app-defaults/Oneko lib/X11/oneko/cat1.xpm lib/X11/oneko/cat2.xpm lib/X11/oneko/mouse.xpm .... Refer to the man:pkg-create[8] manual page for details on the packing list. [NOTE] ==== It is recommended to keep all the filenames in this file sorted alphabetically. It will make verifying changes when upgrading the port much easier. The sorting should be applied after variable expansion takes place. The framework does this correctly when the package list is crossref:plist[plist-autoplist,generated automatically]. ==== [TIP] ==== Creating a packing list manually can be a very tedious task. If the port installs a large numbers of files, crossref:plist[plist-autoplist,creating the packing list automatically] might save time. ==== There is only one case when [.filename]#pkg-plist# can be omitted from a port. If the port installs just a handful of files, list them in `PLIST_FILES`, within the port's [.filename]#Makefile#. For instance, we could get along without [.filename]#pkg-plist# in the above [.filename]#oneko# port by adding these lines to the [.filename]#Makefile#: [.programlisting] .... PLIST_FILES= bin/oneko \ man/man1/oneko.1.gz \ lib/X11/app-defaults/Oneko \ lib/X11/oneko/cat1.xpm \ lib/X11/oneko/cat2.xpm \ lib/X11/oneko/mouse.xpm .... [NOTE] ==== Usage of `PLIST_FILES` should not be abused. When looking for the origin of a file, people usually try to grep through the [.filename]#pkg-plist# files in the ports tree. Listing files in `PLIST_FILES` in the [.filename]#Makefile# makes that search more difficult. ==== [TIP] ==== If a port needs to create an empty directory, or creates directories outside of [.filename]#${PREFIX}# during installation, refer to crossref:plist[plist-dir-cleaning,Cleaning Up Empty Directories] for more information. ==== [TIP] ==== As `PLIST_FILES` is a man:make[1] variable, any entry with spaces must be quoted. For example, if using keywords described in man:pkg-create[8] and crossref:plist[plist-keywords,Expanding Package List with Keywords], the entry must be quoted. [.programlisting] .... PLIST_FILES= "@sample ${ETCDIR}/oneko.conf.sample" .... ==== Later we will see how [.filename]#pkg-plist# and `PLIST_FILES` can be used to fulfill crossref:plist[plist,more sophisticated tasks]. [[porting-checksum]] == Creating the Checksum File Just type `make makesum`. The ports framework will automatically generate [.filename]#distinfo#. Do not try to generate the file manually. [[porting-testing]] == Testing the Port Make sure that the port rules do exactly what is desired, including packaging up the port. These are the important points to verify: * [.filename]#pkg-plist# does not contain anything not installed by the port. * [.filename]#pkg-plist# contains everything that is installed by the port. * The port can be installed using the `install` target. This verifies that the install script works correctly. * The port can be deinstalled properly using the `deinstall` target. This verifies that the deinstall script works correctly. * The port only has access to network resources during the `fetch` target phase. This is important for package builders, such as package:ports-mgmt/poudriere[]. * Make sure that `make package` can be run as a normal user (that is, not as `root`). If that fails, the software may need to be patched. See also crossref:uses[uses-fakeroot,`fakeroot`] and crossref:uses[uses-uidfix,`uidfix`]. [.procedure] .Procedure: Recommended Test Ordering . `make stage` . `make stage-qa` . `make package` . `make install` . `make deinstall` . `make package` (as user) Make certain no warnings are shown in any of the stages. Thorough automated testing can be done with package:ports-mgmt/poudriere[] from the Ports Collection, see crossref:testing[testing-poudriere,poudriere] for more information. It maintains `jails` where all of the steps shown above can be tested without affecting the state of the host system. [[porting-portlint]] == Checking the Port with `portlint` Please use `portlint` to see if the port conforms to our guidelines. The package:ports-mgmt/portlint[] program is part of the ports collection. In particular, check that the crossref:porting-samplem[porting-samplem,Makefile] is in the right shape and the crossref:porting-pkgname[porting-pkgname,package] is named appropriately. [IMPORTANT] ==== Do not blindly follow the output of `portlint`. It is a static lint tool and sometimes gets things wrong. ==== [[porting-submitting]] == Submitting the New Port Before submitting the new port, read the crossref:porting-dads[porting-dads,DOs and DON'Ts] section. Once happy with the port, the only thing remaining is to put it in the main FreeBSD ports tree and make everybody else happy about it too. [IMPORTANT] ==== We do not need the [.filename]#work# directory or the [.filename]#pkgname.txz# package, so delete them now. ==== Next, create a man:patch[1], file. Assuming the port is called `oneko` and is in the `games` category. [[porting-submitting-diff]] .Creating a [.filename]#.diff# for a New Port [example] ==== Add all the files with `git add .`, then review the diff with `git diff`. For example: [source,shell] .... % git add . % git diff --staged .... Make sure that all required files are included, then commit the change to your local branch and generate a patch with `git format-patch` [source,shell] .... % git commit % git format-patch origin/main .... Patch generated with `git format-patch` will include author identity and email addresses, making it easier for developers to apply (with `git am`) and give proper credit. [IMPORTANT] -**** +==== To make it easier for committers to apply the patch on their working copy of the ports tree, please generate the [.filename]#.diff# from the base of your ports tree. -**** +==== ==== Submit [.filename]#oneko.diff# with the https://bugs.freebsd.org/submit/[bug submission form]. Use product "Ports & Packages", component "Individual Port(s)", and follow the guidelines shown there. Add a short description of the program to the Description field of the PR (perhaps a short version of `COMMENT`), and remember to add [.filename]#oneko.diff# as an attachment. [NOTE] ==== Giving a good description in the summary of the problem report makes the work of port committers and triagers a lot easier. The expected format for new ports is "[NEW PORT] _category/portname short description of the port_". Using this scheme makes it easier and faster to begin the work of committing the new port. ==== After submitting the port, please be patient. The time needed to include a new port in FreeBSD can vary from a few days to a few months. A simple search form of the Problem Report database can be searched at https://bugs.freebsd.org/bugzilla/query.cgi[]. To get a listing of _open_ port PRs, select _Open_ and _Ports & Packages_ in the search form, then click btn:[Search]. After looking at the new port, we will reply if necessary, and commit it to the tree. The submitter's name will also be added to the list of extref:{contributors}[Additional FreeBSD Contributors, contrib-additional] and other files. [IMPORTANT] ==== Previously it was possible to submit patches for new ports using a man:shar[1] file; this is no longer the case with the evolution of man:git[1]. Committers no longer accept man:shar[1] files as their use is prone to mistake and does not add the relevant entry in the category's [.filename]#Makefile#. ==== diff --git a/documentation/content/en/books/porters-handbook/testing/_index.adoc b/documentation/content/en/books/porters-handbook/testing/_index.adoc index c854994f6f..9cf5786d20 100644 --- a/documentation/content/en/books/porters-handbook/testing/_index.adoc +++ b/documentation/content/en/books/porters-handbook/testing/_index.adoc @@ -1,666 +1,666 @@ --- title: Chapter 10. Testing the Port prev: books/porters-handbook/pkg-files next: books/porters-handbook/upgrading description: Testing a FreeBSD Port tags: ["testing", "port", "Portclippy", "Portfmt", "Portlint", "poudriere", "sets"] showBookMenu: true weight: 10 params: path: "/books/porters-handbook/testing/" --- [[testing]] = Testing the Port :doctype: book :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumoffset: 10 :partnums: :source-highlighter: rouge :experimental: :images-path: books/porters-handbook/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] [[make-describe]] == Running `make describe` Several of the FreeBSD port maintenance tools, such as man:portupgrade[1], rely on a database called [.filename]#/usr/ports/INDEX# which keeps track of such items as port dependencies. [.filename]#INDEX# is created by the top-level [.filename]#ports/Makefile# via `make index`, which descends into each port subdirectory and executes `make describe` there. Thus, if `make describe` fails in any port, no one can generate [.filename]#INDEX#, and many people will quickly become unhappy. [NOTE] ==== It is important to be able to generate this file no matter what options are present in [.filename]#make.conf#, so please avoid doing things such as using `.error` statements when (for instance) a dependency is not satisfied. (See crossref:porting-dads[dads-dot-error,Avoid Use of the `.error` Construct].) ==== If `make describe` produces a string rather than an error message, everything is probably safe. See [.filename]#bsd.port.mk# for the meaning of the string produced. Also note that running a recent version of `portlint` (as specified in the next section) will cause `make describe` to be run automatically. [[make-test]] == Running `make test` Even if the port builds fine, it is a good idea to ensure that the software correctly does what it is supposed to do. If the original upstream project provides tests along with the software, it is a good idea to run them and check everything works as expected. A port can enable tests automatically by using the `TEST_TARGET` variable. When set, this variable contains the name of the testing target of the port. This is usually just `test` but other names include `tests`, `check` or for specific cases things like `run_tests.py`. In addition to the `TEST_TARGET` variable the framework provides the following variables to control the tests execution: * `TEST_WRKSRC` is the directory to do the tests in. * `TEST_ENV` contains additional variables to be passed to the test stage. * `TEST_ARGS` contains any extra arguments passed to the test stage. Examples of use of these variables can be found in package:cad/xyce[], package:www/libjwt[] and others. [NOTE] ==== Please make sure that tests do not break when updating a port. ==== [[testing-portclippy]] == Portclippy / Portfmt Those tools come from package:ports-mgmt/portfmt[]. Portclippy is a linter that checks if variables in the [.filename]#Makefile# are in the correct order according to crossref:order[porting-order,Order of Variables in Port Makefiles]. Portfmt is a tool for automatically formatting [.filename]#Makefile#. [[testing-portlint]] == Portlint Do check the port with crossref:quick-porting[porting-portlint,`portlint`] before submitting or committing it. `portlint` warns about many common errors, both functional and stylistic. For a new port, `portlint -A` is the most thorough; for an existing port, `portlint -C` is sufficient. Since `portlint` uses heuristics to try to figure out errors, it can produce false positive warnings. In addition, occasionally something that is flagged as a problem really cannot be done in any other way due to limitations in the ports framework. When in doubt, the best thing to do is ask on {freebsd-ports}. [[testing-porttools]] == Port Tools The package:ports-mgmt/porttools[] program is part of the Ports Collection. `port` is the front-end script, which can help simplify the testing job. Whenever a new port or an update to an existing one needs testing, use `port test` to test the port, including the crossref:testing[testing-portlint,`portlint`] checking. This command also detects and lists any files that are not listed in [.filename]#pkg-plist#. For example: [source,shell] .... # port test /usr/ports/net/csup .... [[porting-prefix]] == `PREFIX` and `DESTDIR` `PREFIX` determines where the port will be installed. It defaults to [.filename]#/usr/local#, but can be set by the user to a custom path like [.filename]#/opt#. The port must respect the value of this variable. `DESTDIR`, if set by the user, determines the complete alternative environment, usually a jail or an installed system mounted somewhere other than [.filename]#/#. A port will actually install into [.filename]#DESTDIR/PREFIX#, and register with the package database in [.filename]#DESTDIR/var/db/pkg#. `DESTDIR` is handled automatically by the ports infrastructure with man:chroot[8]. There is no need for modifications or any extra care to write `DESTDIR`-compliant ports. The value of `PREFIX` will be set to `LOCALBASE` (defaulting to [.filename]#/usr/local#). If `USE_LINUX_PREFIX` is set, `PREFIX` will be `LINUXBASE` (defaulting to [.filename]#/compat/linux#). Avoiding hard-coded [.filename]#/usr/local# paths in the source makes the port much more flexible and able to cater to the needs of other sites. Often, this can be accomplished by replacing occurrences of [.filename]#/usr/local# in the port's various [.filename]##Makefile##s with `${PREFIX}`. This variable is automatically passed down to every stage of the build and install processes. Make sure the application is not installing things in [.filename]#/usr/local# instead of `PREFIX`. A quick test for such hard-coded paths is: [source,shell] .... % make clean; make package PREFIX=/var/tmp/`make -V PORTNAME` .... If anything is installed outside of `PREFIX`, the package creation process will complain that it cannot find the files. In addition, it is worth checking the same with the stage directory support (see crossref:special[staging,Staging]): [source,shell] .... % make stage && make check-plist && make stage-qa && make package .... * `check-plist` checks for files missing from the plist, and files in the plist that are not installed by the port. * `stage-qa` checks for common problems like bad shebang, symlinks pointing outside the stage directory, setuid files, and non-stripped libraries... These tests will not find hard-coded paths inside the port's files, nor will it verify that `LOCALBASE` is being used to correctly refer to files from other ports. The temporarily installed port in [.filename]#/var/tmp/`make -V PORTNAME`# must be tested for proper operation to make sure there are no problems with paths. `PREFIX` must not be set explicitly in a port's [.filename]#Makefile#. Users installing the port may have set `PREFIX` to a custom location, and the port must respect that setting. Refer to programs and files from other ports with the variables mentioned above, not explicit pathnames. For instance, if the port requires a macro `PAGER` to have the full pathname of `less`, do not use a literal path of [.filename]#/usr/local/bin/less#. Instead, use `${LOCALBASE}`: [.programlisting] .... -DPAGER=\"${LOCALBASE}/bin/less\" .... The path with `LOCALBASE` is more likely to still work if the system administrator has moved the whole [.filename]#/usr/local# tree somewhere else. [TIP] ==== All these tests are done automatically when running `poudriere testport` or `poudriere bulk -t`. It is highly recommended that every ports contributor install and test their ports with it. See crossref:testing[testing-poudriere, poudriere] for more information. ==== [[testing-poudriere]] == poudriere For a ports contributor, poudriere is one of the most important and helpful testing and build tools. Its main features include: * Bulk building of the entire ports tree, specific subsets of the ports tree, or a single port including its dependencies * Automatic packaging of build results * Generation of build log files per port * Providing a signed man:pkg[8] repository * Testing of port builds before submitting a patch to the FreeBSD bug tracker or committing to the ports tree * Testing for successful ports builds using different options Because poudriere performs its building in a clean man:jail[8] environment and uses man:zfs[8] features, it has several advantages over traditional testing on the host system: * No pollution of the host environment: No leftover files, no accidental removals, no changes of existing configuration files. * Verify [.filename]#pkg-plist# for missing or superfluous entries * Ports committers sometimes ask for a poudriere log alongside a patch submission to assess whether the patch is ready for integration into the ports tree It is also quite straightforward to set up and use, has no dependencies, and will run on any supported FreeBSD release. This section shows how to install, configure, and run poudriere as part of the normal workflow of a ports contributor. The examples in this section show a default file layout, as standard in FreeBSD. Substitute any local changes accordingly. The ports tree, represented by `${PORTSDIR}`, is located in [.filename]#/usr/ports#. Both `${LOCALBASE}` and `${PREFIX}` are [.filename]#/usr/local# by default. [[testing-poudriere-installing]] === Installing poudriere poudriere is available in the ports tree in package:ports-mgmt/poudriere[]. It can be installed using man:pkg[8] or from ports: [source,shell] .... # pkg install poudriere .... or [source,shell] .... # make -C /usr/ports/ports-mgmt/poudriere install clean .... There is also a work-in-progress version of poudriere which will eventually become the next release. It is available in package:ports-mgmt/poudriere-devel[]. This development version is used for the official FreeBSD package builds, so it is well tested. It often has newer interesting features. A ports committer will want to use the development version because it is what is used in production, and has all the new features that will make sure everything is exactly right. A contributor will not necessarily need those as the most important fixes are backported to released version. The main reason for the use of the development version to build the official package is because it is faster, in a way that will shorten a full build from 18 hours to 17 hours when using a high end 32 CPU server with 128GB of RAM. Those optimizations will not matter a lot when building ports on a desktop machine. [[testing-poudriere-setup]] === Setting Up poudriere The port installs a default configuration file, [.filename]#/usr/local/etc/poudriere.conf#. Each parameter is documented in the configuration file. Here is a minimal example config file: [.programlisting] .... ZPOOL=zroot BASEFS=/usr/local/poudriere DISTFILES_CACHE=/usr/ports/distfiles RESOLV_CONF=/etc/resolv.conf .... `ZPOOL`:: The name of the ZFS storage pool which poudriere shall use. Must be listed in the output of `zpool status`. `BASEFS`:: The root mount point for poudriere file systems. This entry will cause poudriere to mount `tank/poudriere` to `/poudriere`. `DISTFILES_CACHE`:: Defines where distfiles are stored. In this example, poudriere and the host share the distfiles storage directory. This avoids downloading tarballs which are already present on the system. Please create this directory if it does not already exist so that poudriere can find it. `RESOLV_CONF`:: Use the host [.filename]#/etc/resolv.conf# inside jails for DNS. This is needed so jails can resolve the URLs of distfiles when downloading. It is not needed when using a proxy. Refer to the default configuration file for proxy configuration. [[testing-poudriere-create-jails]] === Creating poudriere Jails Create the base jails which poudriere will use for building: [source,shell] .... # poudriere jail -c -j 143Ramd64 -v 14.3-RELEASE -a amd64 .... Fetch a `14.3-RELEASE` for `amd64` from the HTTPS server given by `FREEBSD_HOST` in [.filename]#poudriere.conf#, create the zfs file system `tank/poudriere/jails/143Ramd64`, mount it on [.filename]#/poudriere/jails/143Ramd64# and extract the `14.3-RELEASE` tarballs into this file system. [source,shell] .... # poudriere jail -c -j 13i386 -v stable/13 -a i386 -m git+https .... Create `tank/poudriere/jails/13i386`, mount it on [.filename]#/poudriere/jails/13i386#, then check out the tip of the Git branch of `FreeBSD-13-STABLE` from `GIT_HOST` in [.filename]#poudriere.conf# or the default `git.freebsd.org` into [.filename]#/poudriere/jails/13i386/usr/src#, then complete a `buildworld` and install it into [.filename]#/poudriere/jails/13i386#. [NOTE] ==== While it is possible to build a newer version of FreeBSD on an older version, most of the time it will not run. For example, if a `stable/14` jail is needed, the host will have to run `stable/14` too. Running `14.3-RELEASE` is not enough. ==== [NOTE] ==== To create a poudriere jail for `16.0-CURRENT`: [source,shell] .... # poudriere jail -c -j 16amd64 -v main -a amd64 -m git+https .... In order to run a `16.0-CURRENT` poudriere jail the host must be running `16.0-CURRENT`. In general, newer kernels can build and run older jails. For instance, a `16.0-CURRENT` kernel can build and run a `14.3-STABLE` if the `COMPAT_FREEBSD14` kernel option was compiled in (on by default in `16.0-CURRENT`[.filename]#GENERIC# kernel config). ==== A list of jails currently known to poudriere can be shown with `poudriere jail -l`: [source,shell] .... # poudriere jail -l JAILNAME VERSION ARCH METHOD 143Ramd64 14.3-RELEASE amd64 http 13i386 13.5-STABLE i386 git+https .... [[testing-poudriere-maintaining-jails]] === Keeping poudriere Jails Updated Managing updates is very straightforward. The command: [source,shell] .... # poudriere jail -u -j JAILNAME .... updates the specified jail to the latest version available. For FreeBSD releases, update to the latest patchlevel with man:freebsd-update[8]. For FreeBSD versions built from source, update to the latest git revision in the branch. [TIP] ==== For jails employing a `git+*` method, it is helpful to add `-J _NumberOfParallelBuildJobs_` to speed up the build by increasing the number of parallel compile jobs used. For example, if the building machine has 6 CPUs, use: [source,shell] .... # poudriere jail -u -J 6 -j JAILNAME .... ==== [[testing-poudriere-ports-tree]] === Setting Up Ports Trees for Use with poudriere There are multiple ways to use ports trees in poudriere. The most straightforward way is to have poudriere create a default ports tree for itself, using link:{handbook}mirrors/#git[Git]: [source,shell] .... # poudriere ports -c -m git+https -B main .... These commands create `tank/poudriere/ports/default`, mount it on [.filename]#/poudriere/ports/default#, and populate it using Git. Afterward it is included in the list of known ports trees: [source,shell] .... # poudriere ports -l PORTSTREE METHOD TIMESTAMP PATH default git+https 2025-07-20 04:23:56 /poudriere/ports/default .... [NOTE] ==== Note that the "default" ports tree is special. Each of the build commands explained later will implicitly use this ports tree unless specifically specified otherwise. To use another tree, add `-p _treename_` to the commands. ==== The best way to deal with local modifications for a ports contributor is to use link:{handbook}mirrors/#git[Git]. As with the creation of jails, it is possible to use a different method for creating the ports tree. To add an additional ports tree for testing local modifications and ports development, checking out the tree via git (as described above) is preferable. [[testing-poudriere-ports-tree-manual]] === Using Manually Managed Ports Trees with poudriere Depending on the workflow, it can be extremely helpful to use ports trees which are maintained manually. For instance, if there is a local copy of the ports tree in [.filename]#/work/ports#, point poudriere to the location: [source,shell] .... # poudriere ports -c -m null -M /work/ports -p development .... This will be listed in the table of known trees: [source,shell] .... # poudriere ports -l PORTSTREE METHOD TIMESTAMP PATH development null 2025-07-20 05:06:33 /work/ports .... [NOTE] ==== The dash or `null` in the `METHOD` column means that poudriere will not update or change this ports tree, ever. It is completely up to the user to maintain this tree, including all local modifications that may be used for testing new ports and submitting patches. ==== [[testing-poudriere-ports-tree-updating]] === Keeping poudriere Ports Trees Updated As straightforward as with jails described earlier: [source,shell] .... # poudriere ports -u -p PORTSTREE .... Will update the given _PORTSTREE_, one tree given by the output of `poudriere -l`, to the latest revision available on the official servers. [NOTE] ==== Ports trees without a method, see crossref:testing[testing-poudriere-ports-tree-manual, Using Manually Managed Ports Trees with poudriere], cannot be updated like this and must be updated manually by the porter. ==== [[testing-poudriere-testing-ports]] === Testing Ports After jails and ports trees have been set up, the result of a contributor's modifications to the ports tree can be tested. For example, local modifications to the package:www/firefox[] port located in [.filename]#/work/ports/www/firefox# can be tested in the previously created 14.3-RELEASE jail: [source,shell] .... # poudriere testport -j 143Ramd64 -p development -o www/firefox .... This will build all dependencies of Firefox. If a dependency has been built previously and is still up-to-date, the pre-built package is installed. If a dependency has no up-to-date package, one will be built with default options in a jail. Then Firefox itself is built. The complete build of every port is logged to [.filename]#/poudriere/data/logs/bulk/143Ri386-development/build-time/logs#. The directory name `143Ri386-development` is derived from the arguments to `-j` and `-p`, respectively. For convenience, a symbolic link [.filename]#/poudriere/data/logs/bulk/143Ri386-development/latest# is also maintained. The link points to the latest _build-time_ directory. Also in this directory is an [.filename]#index.html# for observing the build process with a web browser. By default, poudriere cleans up the jails and leaves log files in the directories mentioned above. To ease investigation, jails can be kept running after the build by adding `-i` to `testport`: [source,shell] .... # poudriere testport -j 143Ramd64 -p development -i -o www/firefox .... After the build completes, and regardless of whether it was successful, a shell is provided within the jail. The shell is used to investigate further. poudriere can be told to leave the jail running after the build finishes with `-I`. poudriere will show the command to run when the jail is no longer needed. It is then possible to man:jexec[8] into it: [source,shell] .... # poudriere testport -j 143Ramd64 -p development -I -o www/firefox [...] ====>> Installing local Pkg repository to /usr/local/etc/pkg/repos ====>> Leaving jail 143Ramd64-development-n running, mounted at /poudriere/data/.m/143Ramd64-development/ref for interactive run testing ====>> To enter jail: jexec 143Ramd64-development-n env -i TERM=$TERM /usr/bin/login -fp root ====>> To stop jail: poudriere jail -k -j 143Ramd64 -p development # jexec 143Ramd64-development-n env -i TERM=$TERM /usr/bin/login -fp root # [do some stuff in the jail] # exit # poudriere jail -k -j 143Ramd64 -p development ====>> Umounting file systems .... An integral part of the FreeBSD ports build infrastructure is the ability to tweak ports to personal preferences with options. These can be tested with poudriere as well. Adding the `-c`: [source,shell] .... # poudriere testport -j 143Ramd64 -c -o www/firefox .... Presents the port configuration dialog before the port is built. The ports given after `-o` in the format `_category_/_portname_` will use the specified options, all dependencies will use the default options. Testing dependent ports with non-default options can be accomplished using sets, see crossref:testing[testing-poudriere-sets, Using Sets]. [TIP] ==== When testing ports where [.filename]#pkg-plist# is altered during build depending on the selected options, it is recommended to perform a test run with all options selected _and_ one with all options deselected. ==== [[testing-poudriere-sets]] === Using Sets For all actions involving builds, a so-called _set_ can be specified using `-z _setname_`. A set refers to a fully independent build. This allows, for instance, usage of `testport` with non-standard options for the dependent ports. To use sets, poudriere expects an existing directory structure similar to `PORT_DBDIR`, defaults to [.filename]#/var/db/ports# in its configuration directory. This directory is then man:nullfs[5]-mounted into the jails where the ports and their dependencies are built. Usually a suitable starting point can be obtained by recursively copying the existing `PORT_DBDIR` to [.filename]#/usr/local/etc/poudriere.d/jailname-portname-setname-options#. This is described in detail in man:poudriere[8]. For instance, testing package:www/firefox[] in a specific set named `devset`, add the `-z devset` parameter to the `testport` command: [source,shell] .... # poudriere testport -j 143Ramd64 -p development -z devset -o www/firefox .... This will look for the existence of these directories in this order: * [.filename]#/usr/local/etc/poudriere.d/143Ramd64-development-devset-options# * [.filename]#/usr/local/etc/poudriere.d/143Ramd64-devset-options# * [.filename]#/usr/local/etc/poudriere.d/143Ramd64-development-options# * [.filename]#/usr/local/etc/poudriere.d/devset-options# * [.filename]#/usr/local/etc/poudriere.d/development-options# * [.filename]#/usr/local/etc/poudriere.d/143Ramd64-options# * [.filename]#/usr/local/etc/poudriere.d/options# From this list, poudriere man:nullfs[5]-mounts the _first existing_ directory tree into the [.filename]#/var/db/ports# directory of the build jails. Hence, all custom options are used for all the ports during this run of `testport`. After the directory structure for a set is provided, the options for a particular port can be altered. For example: [source,shell] .... # poudriere options -c www/firefox -z devset .... The configuration dialog for package:www/firefox[] is shown, and options can be edited. The selected options are saved to the `devset` set. [NOTE] ==== poudriere is very flexible in the option configuration. poudriere can be set for particular jails, ports trees, and for multiple ports by one command. Refer to man:poudriere[8] for details. ==== [[testing-poudriere-make-conf]] === Providing a Custom [.filename]#make.conf# File Similar to using sets, poudriere will also use a custom [.filename]#make.conf# if it is provided. No special command line argument is necessary. Instead, poudriere looks for existing files matching a name scheme derived from the command line. For instance: [source,shell] .... # poudriere testport -j 143Ramd64 -p development -z devset -o www/firefox .... causes poudriere to check for the existence of these files in this order: * [.filename]#/usr/local/etc/poudriere.d/make.conf# * [.filename]#/usr/local/etc/poudriere.d/devset-make.conf# * [.filename]#/usr/local/etc/poudriere.d/development-make.conf# * [.filename]#/usr/local/etc/poudriere.d/143Ramd64-make.conf# * [.filename]#/usr/local/etc/poudriere.d/143Ramd64-development-make.conf# * [.filename]#/usr/local/etc/poudriere.d/143Ramd64-devset-make.conf# * [.filename]#/usr/local/etc/poudriere.d/143Ramd64-development-devset-make.conf# Unlike with sets, all of the found files will be appended, _in that order_, into one [.filename]#make.conf# inside the build jails. It is hence possible to have general make variables, intended to affect all builds in [.filename]#/usr/local/etc/poudriere.d/make.conf#. Special variables, intended to affect only certain jails or sets can be set in specialised [.filename]#make.conf# files, such as [.filename]#/usr/local/etc/poudriere.d/143Ramd64-development-devset-make.conf#. [[testing-poudriere-sets-perl]] .Using [.filename]#make.conf# to Change Default Perl [example] ==== To build a set with a non default Perl version, for example, `5.20`, using a set named `perl5-20`, create a [.filename]#perl5-20-make.conf# with this line: [.programlisting] .... DEFAULT_VERSIONS+= perl=5.20 .... [NOTE] -**** +===== Note the use of `+=` so that if the variable is already set in the default [.filename]#make.conf# its content will not be overwritten. -**** +===== ==== [[testing-poudriere-pruning-distfiles]] === Pruning no Longer Needed Distfiles poudriere comes with a built-in mechanism to remove outdated distfiles that are no longer used by any port of a given tree. The command [source,shell] .... # poudriere distclean -p portstree .... will scan the distfiles folder, `DISTFILES_CACHE` in [.filename]#poudriere.conf#, versus the ports tree given by the `-p _portstree_` argument and prompt for removal of those distfiles. To skip the prompt and remove all unused files unconditionally, the `-y` argument can be added: [source,shell] .... # poudriere distclean -p portstree -y .... [[testing-debugging-ports]] == Debugging ports Sometimes things go wrong and the port fails at run time. The framework provides some facilities to help in debugging ports. These helpers are limited since the way of debugging a port heavily depends on the technology used. The following variables help with debugging ports: * `WITH_DEBUG`. If set, ports are built with debugging symbols. * `WITH_DEBUG_PORTS`. Specifies a list of ports to be built with `WITH_DEBUG` set. * `DEBUG_FLAGS`. Used to specify additional flags to `CFLAGS`. Defaults to `-g`. When `WITH_DEBUG` is set, either globally or for a list of ports, the resulting binaries are not stripped. These variables can be specified in [.filename]#make.conf# or in the command line: [source,shell] .... # cd category/port && make -DWITH_DEBUG DEBUG_FLAGSS="-g -O0" .... [NOTE] ==== If the port is built using package:ports-mgmt/poudriere[] the debugging variables must be specified in poudriere's [.filename]#make.conf# and not in [.filename]#/etc/make.conf#. Refer to package:ports-mgmt/poudriere[] documentation for details. ==== Please refer to the debugging information in the extref:{developers-handbook}tools[Developer's Handbook, debugging] for more details about the debugging tools available. diff --git a/documentation/content/es/books/handbook/advanced-networking/_index.adoc b/documentation/content/es/books/handbook/advanced-networking/_index.adoc index 31a0325853..cc9fbc7116 100644 --- a/documentation/content/es/books/handbook/advanced-networking/_index.adoc +++ b/documentation/content/es/books/handbook/advanced-networking/_index.adoc @@ -1,2099 +1,2099 @@ --- description: 'Redes Avanzadas en FreeBSD: conceptos básicos de gateways y rutas, CARP, cómo configurar múltiples VLANs en FreeBSD, etc' next: books/handbook/partv part: 'IV. Comunicación de Red' params: path: "/books/handbook/advanced-networking/" prev: books/handbook/firewalls showBookMenu: 'true' tags: ["Advanced Networking", "Handbook", "gateway", "routes", "wireless", "tethering", "bluetooth", "bridging", "CARP", "VLAN"] title: 'Capítulo 34. Redes Avanzadas' weight: 39 --- [[advanced-networking]] = Redes Avanzadas :doctype: book :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumoffset: 34 :partnums: :source-highlighter: rouge :experimental: :images-path: books/handbook/advanced-networking/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] [[advanced-networking-synopsis]] == Sinopsis Este capítulo cubre cierto número de temas avanzados de redes. Después de leer este capítulo, sabrás: * Lo básico acerca de gateways y rutas. * Cómo configurar tethering por USB. * Cómo configurar dispositivos IEEE(R) 802.11 y Bluetooth(R). * Cómo hacer que FreeBSD actúe como un puente. * Cómo configurar arranque por red PXE. * Cómo habilitar y utilizar las características del Common Address Redundancy Protocol (CARP) en FreeBSD. * Cómo configurar múltiples VLANs en FreeBSD. * Configurar unos auriculares con micrófono vía bluetooth. Antes de leer este capítulo, deberías: * Comprender lo básico acerca de los scripts [.filename]#/etc/rc#. * Estar familiarizado con la terminología básica de red. * Entendiendo la configuración básica de red en FreeBSD (crossref:network[network,FreeBSD network]). * Saber cómo configurar e instalar un nuevo kernel de FreeBSD (crossref:kernelconfig[kernelconfig,Configurando el Núcleo de FreeBSD]). * Cómo instalar software adicional de terceros (crossref:ports[ports,Instalando Aplicaciones: Paquetes y Ports]). [[network-routing]] == Gateways y Rutas _Routing_ es el mecanismo que permite a un sistema encontrar el camino de red a otro sistema. Una _ruta_ es un par de direcciones definido las cuales representan el "destino" y el "gateway". La ruta indica que cuando se trata de llegar a un destino especificado, se deben enviar los paquetes a través del gateway especificado. Hay tres tipos de destinos: hosts individuales, subredes, y "default". La "ruta por defecto" se utiliza si no se puede aplicar ninguna otra ruta. También hay tres tipos de gateways: hosts individuales, interfaces, también llamados enlaces, y direcciones Ethernet (MAC). Las rutas conocidas se almacenan en una tabla de enrutamiento. Esta sección proporciona una visión general de aspectos básicos de enrutado. Luego muestra cómo configurar un sistema FreeBSD como un router y proporciona algunas pistas para resolver problemas. [[network-routing-default]] === Enrutamiento Básico Para ver la tabla de enrutamiento de un sistema FreeBSD, usa man:netstat[1]: [source, shell] .... % netstat -r Routing tables Internet: Destination Gateway Flags Refs Use Netif Expire default outside-gw UGS 37 418 em0 localhost localhost UH 0 181 lo0 test0 0:e0:b5:36:cf:4f UHLW 5 63288 re0 77 10.20.30.255 link#1 UHLW 1 2421 example.com link#1 UC 0 0 host1 0:e0:a8:37:8:1e UHLW 3 4601 lo0 host2 0:e0:a8:37:8:1e UHLW 0 5 lo0 => host2.example.com link#1 UC 0 0 224 link#1 UC 0 0 .... Las entradas en este ejemplo son como sigue: Defecto:: La primera ruta en esta tabla especifica la ruta por defecto (`default`). Cuando el sistema local necesita conectarse a un host remoto, comprueba la tabla de enrutamiento para determinar si existe un camino. Si el host remoto tiene una entrada en la tabla, el sistema comprueba si puede conectar utilizando el interfaz especificado en dicha entrada. + Si el destino no tiene una entrada, o si todos los caminos conocidos fallan, el sistema utiliza la entrada para el enrutamiento por defecto. Para hosts en la red de área local, el campo `Gateway` en la ruta por defecto se establece al sistema que tiene una conexión directa a Internet. Cuando se lee esta entrada, verifica que la columna `Flags` indica que el gateway se puede usar (`UG`). + La ruta por defecto para una máquina que está funcionando como gateway para el mundo exterior será la máquina gateway del Proveedor de Servicio de Internet (ISP). localhost:: La segunda ruta es `localhost`. El interfaz especificado en la columna `Netif` para `localhost` es [.filename]#lo0#, también conocido como el dispositivo loopback. Esto indica que todo el tráfico para este destino debería ser interno, en lugar de enviarlo a través de la red. Dirección MAC:: Las direcciones que comienzan con `0:e0` son direcciones MAC. FreeBSD identificará automáticamente cualquier host, `test0` en el ejemplo, en el Ethernet local y añadirá una ruta para ese host sobre el interfaz Ethernet, [.filename]#re0#. Este tipo de ruta tiene un timeout, mostrado en la columna `Expire`, que es usado si el host no responde en un tiempo determinado. Cuando esto sucede, la ruta a este host será automáticamente borrada. Estos hosts se identifican usando el Routing Information Protocol (RIP), que calcula rutas a los hosts locales basándose en la determinación del camino más corto. subred:: FreeBSD añadirá rutas para la subred local. En este ejemplo, `10.20.30.255` es la dirección de broadcast para la subred `10.20.30` y `example.com` es el nombre de dominio asociado con esa subred. La designación `link#1` hace referencia a la primera tarjeta Ethernet de la máquina. + Hosts en la red local y subredes locales tienen sus rutas configuradas automáticamente por un demonio llamado man:routed[8]. Si no se está ejecutando, sólo existirán las rutas que hayan sido configuradas estáticamente por el administrador. host:: La línea `host1` hace referencia al host mediante su dirección Ethernet. Puesto que es el host que envía, FreeBSD sabe que tienen que usar el interfaz loopback ([.filename]#lo0#) en lugar del interfaz Ethernet. + Las dos líneas `host2` representan alias que se crean utilizando man:ifconfig[8]. El símbolo `=>` después del interfaz [.filename]#lo0# indica que se ha establecido un alias además de la dirección de loopback. Estas rutas sólo se muestran en el host que suporta el alias y el resto de hosts en la red local tendrán una línea `link#1` para esas rutas. 224:: La última línea (subred de destino `224`) tiene que ver con multicasting. Se pueden ver varios atributos para cada ruta en la columna `Flags`. <> resume algunos de estos flags y sus significados: [[routeflags]] .Flags Habituales de la Tabla de Enrutado [cols="1,1", frame="none", options="header"] |=== | Flag | Propósito |U |La ruta está activa (up). |H |La ruta de destino es un único host. |G |Envía cualquier cosa a este destino a través de este gateway, que averiguará a dónde enviarlo a continuación. |S |Esta ruta se ha configurado de forma estática. |C |Clona una nueva ruta basada en esta ruta para que las máquinas puedan conectarse. Este tipo de ruta se usa normalmente para redes locales. |W |La ruta ha sido auto configurada basada en una ruta (clonada) de una red de área local. |L |La ruta incluye referencias a hardware Ethernet (link). |=== En un sistema FreeBSD, la ruta por defecto se puede configurar en [.filename]#/etc/rc.conf# especificando la dirección IP del gateway por defecto: [.programlisting] .... defaultrouter="10.20.30.1" .... También es posible añadir la ruta de forma manual usando `route`: [source, shell] .... # route add default 10.20.30.1 .... Date cuenta de que las rutas añadidas manualmente no persisten entre reinicios. Para más información sobre la manipulación manual de tablas de enrutamiento de red, consulta man:route[8]. [[network-static-routes]] === Configurando un Router con Rutas Estáticas Un sistema FreeBSD se puede configurar como el gateway por defecto, o router, para una red si es un sistema "dual-homed". Un sistema "dual-homed" es una máquina que está en al menos dos redes diferentes. Típicamente cada red se conecta a un interfaz de red separada, aunque se puede usar IP aliasing para enlazar múltiples direcciones, cada una en una subred diferente, a una única interfaz física. Para que el sistema pueda reenviar paquetes entre interfaces, FreeBSD debe ser configurado como un router. Los estándares de Internet y las buenas prácticas de ingeniería evitan que el Proyecto FreeBSD active esta característica por defecto, pero se puede configurar en el arranque añadiendo esta línea a [.filename]#/etc/rc.conf#: [.programlisting] .... gateway_enable="YES" # Set to YES if this host will be a gateway .... Para habilitar el enrutado, establece la variable man:sysctl[8] `net.inet.ip.forwarding` a `1`. Para parar el enrutado, restablece esta variable a `0`. La tabla de enrutamiento de un router necesita rutas adicionales para saber cómo llegar a otras redes. Las rutas se puede añadir manualmente utilizando rutas estáticas o se pueden aprender automáticamente usando un protocolo de enrutamiento. Las rutas estáticas son apropiadas para redes pequeñas y esta sección describe cómo añadir una ruta estática para una red pequeña. [NOTE] ==== Para redes grandes, las rutas estáticas pronto se vuelven impracticables. FreeBSD incluye el demonio de enrutamiento BSD estándar man:routed[8], que proporciona los protocolos de enrutamiento RIP, versiones 1 y 2, y IRDP. Se puede instalar soporte para los protocolos de enrutado BGP y OSPFS usando el paquete o port package:net/quagga[]. ==== Considera la siguiente red: image::static-routes.png[] En este escenario, `RouterA` es una máquina FreeBSD que está actuando como un router para el resto de Internet. Tiene una ruta por defecto establecida a `10.0.0.1` que le permite conectarse con el mundo exterior. `RouterB` ya está configurado para utilizar `192.168.1.1` como su gateway por defecto. Antes de añadir ninguna ruta estática, la tabla de enrutamiento de `RouterA` tiene este aspecto: [source, shell] .... % netstat -nr Routing tables Internet: Destination Gateway Flags Refs Use Netif Expire default 10.0.0.1 UGS 0 49378 xl0 127.0.0.1 127.0.0.1 UH 0 6 lo0 10.0.0.0/24 link#1 UC 0 0 xl0 192.168.1.0/24 link#2 UC 0 0 xl1 .... Con la tabla de enrutamiento actual, `RouterA` no tiene una ruta a la red `192.168.2.0/24`. El siguiente comando añade la red `Internal Net 2` a la tabla de enrutamiento de `RouterA` usando `192.168.1.2` para el siguiente salto: [source, shell] .... # route add -net 192.168.2.0/24 192.168.1.2 .... Ahora, `RouterA` puede alcanzar cualquier host en la red `192.168.2.0/24`. Sin embargo, la información de enrutamiento no persistirá si el sistema FreeBSD se reinicia. Si una ruta estática necesita ser persistente, añádela a [.filename]#/etc/rc.conf#: [.programlisting] .... # Add Internal Net 2 as a persistent static route static_routes="internalnet2" route_internalnet2="-net 192.168.2.0/24 192.168.1.2" .... La variable de configuración `static_routes` es una lista de cadenas separadas por un espacio, donde cada cadena referencia el nombre de una ruta. La variable `route_internalnet2` contiene la ruta estática para el nombre de esa ruta. Usar más de una cadena en `static_routes` crea múltiples rutas estáticas. Lo siguiente muestra un ejemplo de cómo añadir rutas estáticas para las redes `192.168.0.0/24` y `192.168.1.0/24`: [.programlisting] .... static_routes="net1 net2" route_net1="-net 192.168.0.0/24 192.168.0.1" route_net2="-net 192.168.1.0/24 192.168.1.1" .... [[network-routing-troubleshooting]] === Resolución de problemas Cuando se asigna un espacio de direcciones a una red, el proveedor de servicio configura sus propias tablas de enrutamiento de forma que todo el tráfico para la red se enviará a través del enlace para el sitio. Pero ¿cómo saben los sitios externos que tienen que enviar sus paquetes al ISP de la red? Hay un sistema que lleva el control de todos los espacios de direcciones asignados y define sus puntos de conexión a la red principal de Internet, o las líneas troncales que llevan el tráfico por todo el país y alrededor del mundo. Cada máquina troncal tiene una copia de un conjunto maestro de tablas, las cuales dirigen el tráfico para una red particular hacia un portador troncal específico, y de ahí bajando por la cadena de proveedores de servicio hasta que alcanza una red particular. Es tarea del proveedor de servicio avisar a los sitios troncales de que son el punto de conexión, y por tanto el camino de entrada, para un sitio. Esto se conoce como propagación de ruta. A veces, hay algún problema con la propagación de ruta y algunos sitios son incapaces de conectar. Quizás el comando más útil para intentar averiguar dónde se rompe la ruta es `traceroute`. Es útil cuando `ping` falla. Cuando uses `traceroute`, incluye la dirección del host remoto al que conectar. La salida mostrará el gateway junto con el camino que sigue el intento, eventualmente alcanzando el destino, o terminando debido a la falta de conexión. Para más información, consulta man:traceroute[8]. [[network-routing-multicast]] === Consideraciones para Multicast FreeBSD soporta de forma nativa tanto aplicaciones multicast como enrutamiento multicast. Las aplicaciones multicast no necesitan ninguna configuración especial para ejecutarse en FreeBSD. El soporte para enrutamiento multicast requiere que la siguiente opción esté incluida en un kernel personalizado: [.programlisting] .... options MROUTING .... El demonio de enrutamiento multicast, mrouted se puede instalar usando el paquete o port package:net/mrouted[]. Este demonio implementa el protocolo de enrutamiento multicast DVMRP y se configura editando el fichero [.filename]#/usr/local/etc/mrouted.conf# para configurar los túneles y DVMRP. La instalación de mrouted también instala map-mbone y mrinfo, así como sus páginas de manual. Consúltalas para ver ejemplos de configuración. [NOTE] ==== DVMRP ha sido ampliamente sustituido por el protocolo PIM en muchas instalaciones multicast. Consulta man:pim[4] para más información. ==== [[configtuning-virtual-hosts]] == Hosts Virtuales Un uso habitual para FreeBSD es el de proporcionar alojamiento virtual de sitios, donde un servidor aparece en la red como muchos servidores. Esto se consigue asignando múltiples direcciones de red a una única interfaz. Una interfaz dada tiene una dirección "real", y puede tener un determinado número de direcciones "alias". Estos alias se añaden normalmente poniendo entradas alias en [.filename]#/etc/rc.conf#, como se ve en este ejemplo: [source, shell] .... # sysrc ifconfig_fxp0_alias0="inet xxx.xxx.xxx.xxx netmask xxx.xxx.xxx.xxx" .... Las entradas de alias deben empezar con `alias__0__` usando un número secuencial como `alias0`, `alias1`, y así sucesivamente. El proceso de configuración terminará en el primer número que falte. El cálculo de las máscaras de red de los alias es importante. Para una interfaz data, debe haber una dirección que represente correctamente la máscara de la red. Cualquier otra dirección que esté en esta red tiene que tener una más cara con todo ``1``s, expresada como `255.255.255.255` o `0xffffffff`. Por ejemplo, considera el caso donde la interfaz `fxp0` está conectada a dos redes: `10.1.1.0` con máscara de red `255.255.255.0` y `202.0.75.16` con máscara de red`255.255.255.240`. El sistema está configurado para aparecer en los rangos `10.1.1.1` hasta `10.1.1.5` y `202.0.75.17` hasta `202.0.75.20`. Sólo la primera dirección en un rango de red dado debería tener una máscara de red real. Todas las demás (`10.1.1.2` hasta `10.1.1.5` y `202.0.75.18` hasta `202.0.75.20`) se deben configurar con máscara de red `255.255.255.255`. Las siguientes entradas de [.filename]#/etc/rc.conf# configuran correctamente el adaptador para este escenario: [source, shell] .... # sysrc ifconfig_fxp0="inet 10.1.1.1 netmask 255.255.255.0" # sysrc ifconfig_fxp0_alias0="inet 10.1.1.2 netmask 255.255.255.255" # sysrc ifconfig_fxp0_alias1="inet 10.1.1.3 netmask 255.255.255.255" # sysrc ifconfig_fxp0_alias2="inet 10.1.1.4 netmask 255.255.255.255" # sysrc ifconfig_fxp0_alias3="inet 10.1.1.5 netmask 255.255.255.255" # sysrc ifconfig_fxp0_alias4="inet 202.0.75.17 netmask 255.255.255.240" # sysrc ifconfig_fxp0_alias5="inet 202.0.75.18 netmask 255.255.255.255" # sysrc ifconfig_fxp0_alias6="inet 202.0.75.19 netmask 255.255.255.255" # sysrc ifconfig_fxp0_alias7="inet 202.0.75.20 netmask 255.255.255.255" .... Una forma más sencilla de expresar esto es con una lista de rangos de direcciones IP separadas por espacios. A la primera dirección se le asignará la máscara de subred indicada y las demás direcciones tendrán una máscara de subred de `255.255.255.255`. [source, shell] .... # sysrc ifconfig_fxp0_aliases="inet 10.1.1.1-5/24 inet 202.0.75.17-20/28" .... [[network-advanced-wireless]] == Autenticación Inalámbrica Avanzada FreeBSD soporta distintas formas de conectarse a una red inalámbrica. Esta sección describe como realizar autenticación avanzada en una Red Inalámbrica. Para hacer una conexión y autenticación básica a una red inalámbrica la sección crossref:network[wireless-authentication,Conexión y Autenticación a una Red Inalámbrica] en el Capítulo de Red describe como hacerlo. [[network-wireless-wpa-eap-tls]] === WPA with EAP-TLS La segunda forma de utilizar WPA es con un servidor de autenticación 802.1X. En este caso, WPA se llama WPA Enterprise para diferenciarlo del WPA Personal menos seguro. La autenticación en WPA Enterprise se basa en el Extensible Authentication Protocol (EAP). EAP no viene con un método de encriptación. En su lugar, EAP se introduce dentro de un túnel encriptado. Hay muchos métodos de autenticación EAP, pero EAP-TLS, EAP-TTLS, y EAP-PEAP son los más comunes. EAP con Transport Layer Security (EAP-TLS) es un protocolo de autenticación inalámbrica bien soportado ya que fue el primer método EAP certificado por la http://www.wi-fi.org/[Wi-Fi Alliance]. EAP-TLS requiere tres certificados para funcionar: el certificado de Certificate Authority (CA) instalado en todas las máquinas, el certificado de servidor para el servidor de autenticación, y un cliente de certificado para cliente inalámbrico. En este método EAP, tanto el servidor de autenticación como el cliente inalámbrico se autentican entre sí presentando sus respectivos certificados, y luego verificando que estos certificados están firmados por la CA de la organización. Como antes, la configuración se hace mediante [.filename]#/etc/wpa_supplicant.conf#: [.programlisting] .... network={ ssid="freebsdap" <.> proto=RSN <.> key_mgmt=WPA-EAP <.> eap=TLS <.> identity="loader" <.> ca_cert="/etc/certs/cacert.pem" <.> client_cert="/etc/certs/clientcert.pem" <.> private_key="/etc/certs/clientkey.pem" <.> private_key_passwd="freebsdmallclient" <.> } .... <.> Este campo indica el nombre de la red (SSID). <.> Este ejemplo utiliza el protocolo RSN IEEE(R) 802.11i también conocido como WPA2. <.> La línea `key_mgmt` hace referencia al protocolo de gestión de claves que se utiliza. En este ejemplo, es WPA con autenticación EAP. <.> Este campo indica el método EAP para la conexión. <.> El campo `identity` contiene la cadena de identidad para EAP. <.> El campo `ca_cert` indica la ruta al fichero del certificado de CA. Este fichero es necesario para verificar el certificado de servidor. <.> La línea `cliente_cert` da la ruta al fichero de certificado del cliente. Este certificado es único para cada cliente inalámbrico de la red. <.> El campo `private_key` es la ruta al fichero de clave privada del certificado del cliente. <.> El campo `private_key_passwd` contienen la contraseña para la clave privada. Después, añade las siguientes líneas a [.filename]#/etc/rc.conf#: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="WPA DHCP" .... El siguiente paso es levantar la interfaz: [source, shell] .... # service netif start Starting wpa_supplicant. DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 7 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 15 DHCPACK from 192.168.0.20 bound to 192.168.0.254 -- renewal in 300 seconds. wlan0: flags=8843 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.254 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet DS/11Mbps mode 11g status: associated ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 3:128-bit txpower 21.5 bmiss 7 scanvalid 450 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst roaming MANUAL .... También es posible levantar la interfaz manualmente utilizando man:wpa_supplicant[8] y man:ifconfig[8]. [[network-wireless-wpa-eap-ttls]] === WPA with EAP-TTLS Con EAP-TLS, tanto la autenticación de servidor como la de cliente necesitan un certificado. Con EAP-TTLS, el certificado de cliente es opcional. Este método es similar a un servidor web que crea un tunel SSL seguro incluso cuando los visitantes no tienen certificados de cliente. EAP-TTLS utiliza un túnel encriptado con TLS para el transporte seguro de los datos de autenticación. La configuración necesaria se puede añadir a [.filename]#/etc/wpa_supplicant.conf#: [.programlisting] .... network={ ssid="freebsdap" proto=RSN key_mgmt=WPA-EAP eap=TTLS <.> identity="test" <.> password="test" <.> ca_cert="/etc/certs/cacert.pem" <.> phase2="auth=MD5" <.> } .... <.> Este campo especifica el método EAP para la conexión. <.> El campo `identity` contiene la cadena de identidad para la autenticación EAP dentro del túnel encriptado con TLS. <.> El campo `password` contiene la contraseña para la autenticación EAP. <.> El campo `ca_cert` indica la ruta al fichero del certificado de CA. Este fichero es necesario para verificar el certificado de servidor. <.> Este campo especifica el método de autenticación usado en el túnel TLS encriptado. En este ejemplo, se utiliza EAP con MD5-Challenge. La fase de "autenticación interna" se llama habitualmente "phase2". Después, añade las siguientes líneas a [.filename]#/etc/rc.conf#: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="WPA DHCP" .... El siguiente paso es levantar la interfaz: [source, shell] .... # service netif start Starting wpa_supplicant. DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 7 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 15 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 21 DHCPACK from 192.168.0.20 bound to 192.168.0.254 -- renewal in 300 seconds. wlan0: flags=8843 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.254 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet DS/11Mbps mode 11g status: associated ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 3:128-bit txpower 21.5 bmiss 7 scanvalid 450 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst roaming MANUAL .... [[network-wireless-wpa-eap-peap]] === WPA with EAP-PEAP [NOTE] ==== PEAPv0/EAP-MSCHAPv2 es el método PEAP más común. En este capítulo, el término PEAP se usa para referirnos a ese método. ==== Protected EAP (PEAP) se diseñó como una alternativa a EAP-TTLS y es el segundo estándar EAP más usado por detrás de EAP-TLS. En una red con sistemas operativos variados, PEAP debería ser el estándar más soportado por detrás de EAP-TLS. PEAP es similar a EAP-TTLS ya que utiliza un certificado de servidor para autenticar clientes mediante la creación de un túnel TLS encriptado entre el cliente y el servidor de autenticación, el cual protege el subsiguiente intercambio de información de autenticación. La autenticación PEAP es diferente de EAP-TTLS ya que emite el usuario sin encriptar y sólo la contraseña se envía por el túnel TLS encriptado. EAP-TTLS utilizará el túnel TLS tanto para el nombre de usuario como para la contraseña. Añade las siguientes líneas a [.filename]#/etc/wpa_supplicant.conf# para configurar los parámetros relacionados con EAP-PEAP: [.programlisting] .... network={ ssid="freebsdap" proto=RSN key_mgmt=WPA-EAP eap=PEAP <.> identity="test" <.> password="test" <.> ca_cert="/etc/certs/cacert.pem" <.> phase1="peaplabel=0" <.> phase2="auth=MSCHAPV2" <.> } .... <.> Este campo especifica el método EAP para la conexión. <.> El campo `identity` contiene la cadena de identidad para la autenticación EAP dentro del túnel encriptado con TLS. <.> El campo `password` contiene la contraseña para la autenticación EAP. <.> El campo `ca_cert` indica la ruta al fichero del certificado de CA. Este fichero es necesario para verificar el certificado de servidor. <.> Este campo contiene los parámetros para la primera fase de autenticación, el túnel TLS. Según el servidor de autenticación utilizado, especifica una etiqueta concreta para la autenticación. La mayoría de las veces la etiqueta será "cliente EAP encryption" que se establece usando `peaplabel=0`. Se puede encontrar más información en man:wpa_supplicant.confg[5]. <.> Este campo especifica el protocolo de autenticación utilizado en el túnel encriptado con TLS. En el caso de PEAP, es `auth=MSCHAPV2`. Añade lo siguiente a [.filename]#/etc/rc.conf#: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="WPA DHCP" .... Después, levanta la interfaz: [source, shell] .... # service netif start Starting wpa_supplicant. DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 7 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 15 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 21 DHCPACK from 192.168.0.20 bound to 192.168.0.254 -- renewal in 300 seconds. wlan0: flags=8843 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.254 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet DS/11Mbps mode 11g status: associated ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 3:128-bit txpower 21.5 bmiss 7 scanvalid 450 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst roaming MANUAL .... [[wireless-ad-hoc-mode]] == Modo Ad-hoc Inalámbrico El modo IBSS, también llamado modo ad-hoc, está diseñado para comunicaciones punto a punto. Por ejemplo, para establecer una red ad-hoc entre las máquinas `A` y `B`, escoge dos direcciones IP y un SSID. En `A`: [source, shell] .... # ifconfig wlan0 create wlandev ath0 wlanmode adhoc # ifconfig wlan0 inet 192.168.0.1 netmask 255.255.255.0 ssid freebsdap # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether 00:11:95:c3:0d:ac inet 192.168.0.1 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet autoselect mode 11g status: running ssid freebsdap channel 2 (2417 Mhz 11g) bssid 02:11:95:c3:0d:ac country US ecm authmode OPEN privacy OFF txpower 21.5 scanvalid 60 protmode CTS wme burst .... El parámetro `ad-hoc` indica que el interfaz está funcionando en modo IBSS. Ahora `B` debería ser capaz de detecta a `A`: [source, shell] .... # ifconfig wlan0 create wlandev ath0 wlanmode adhoc # ifconfig wlan0 up scan SSID/MESH ID BSSID CHAN RATE S:N INT CAPS freebsdap 02:11:95:c3:0d:ac 2 54M -64:-96 100 IS WME .... La `I` en la salida confirma que `A` está en modo ad-hoc. Ahora, configura `B` con una dirección IP diferente: [source, shell] .... # ifconfig wlan0 inet 192.168.0.2 netmask 255.255.255.0 ssid freebsdap # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.2 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet autoselect mode 11g status: running ssid freebsdap channel 2 (2417 Mhz 11g) bssid 02:11:95:c3:0d:ac country US ecm authmode OPEN privacy OFF txpower 21.5 scanvalid 60 protmode CTS wme burst .... Ahora `A` y `B` están listas para intercambiar información. [[network-wireless-ap]] === Puntos de Acceso Host FreeBSD FreeBSD puede actuar como un Punto de Acceso (AP) lo que elimina la necesidad de comparar un hardware AP o montar una red ad-hoc. Esto puede ser particularmente útil cuando una máquina FreeBSD está actuando como gateway a otra red como Internet. [[network-wireless-ap-basic]] ==== Configuración Básica Antes de configurar una máquina FreeBSD como un AP, el kernel se tiene que configurar con el soporte de red apropiado para la tarjeta inalámbrica así como con los protocolos de seguridad que se utilizarán. Para más detalles, consulta <>. [NOTE] ==== El adaptador de controladores NDIS para controladores de Windows(R) actualmente no soporta operar en modo AP. Sólo los controladores inalámbricos nativos de FreeBSD soportan modo AP. ==== Una vez que se ha cargado el soporte para redes inalámbricas, comprueba si el dispositivo inalámbrico soporta el modo de punto de acceso basado en host, también conocido como modo hostap: [source, shell] .... # ifconfig wlan0 create wlandev ath0 # ifconfig wlan0 list caps drivercaps=6f85edc1 cryptocaps=1f .... Esta salida muestra las capacidades de la tarjeta. La palabra `HOSTAP` confirma que la tarjeta inalámbrica puede actuar como un AP. También se listan varios encriptadores soportados: WEP, TKIP, y AES. Esta información indica qué protocolos de seguridad se pueden utilizar con el AP. El dispositivo inalámbrico sólo puede ser puesto en modo hostap durante la creación del pseudo-dispositivo de red, de forma que si hay una dispositivo creado anteriormente se tiene que destruir primero: [source, shell] .... # ifconfig wlan0 destroy .... después regenerado con la opción correcta antes de establecer otros parámetros: [source, shell] .... # ifconfig wlan0 create wlandev ath0 wlanmode hostap # ifconfig wlan0 inet 192.168.0.1 netmask 255.255.255.0 ssid freebsdap mode 11g channel 1 .... Usa man:ifconfig[8] de nuevo para ver el estado de la interfaz [.filename]#wlan0#: [source, shell] .... # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether 00:11:95:c3:0d:ac inet 192.168.0.1 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet autoselect mode 11g status: running ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode OPEN privacy OFF txpower 21.5 scanvalid 60 protmode CTS wme burst dtimperiod 1 -dfs .... El parámetro `hostap` indica que el interfaz está funcionando en modo punto de acceso basado en host. La configuración del interfaz se puede hacer de forma automática al arrancar añadiendo las siguientes líneas a [.filename]#/etc/rc.conf#: [.programlisting] .... wlans_ath0="wlan0" create_args_wlan0="wlanmode hostap" ifconfig_wlan0="inet 192.168.0.1 netmask 255.255.255.0 ssid freebsdap mode 11g channel 1" .... ==== Punto de Acceso basado en Host Sin Autenticación o Encriptación Aunque no se recomienda ejecutar un AP sin ninguna autenticación o encriptación, es una forma simple de comprobar que el AP funciona. Esta configuración también es importante para depurar problemas en el cliente. Una vez que el AP está configurado, inicia un escaneo desde otra máquina inalámbrica para encontrar el AP: [source, shell] .... # ifconfig wlan0 create wlandev ath0 # ifconfig wlan0 up scan SSID/MESH ID BSSID CHAN RATE S:N INT CAPS freebsdap 00:11:95:c3:0d:ac 1 54M -66:-96 100 ES WME .... La máquina cliente ha encontrado el AP y se puede asociar con él: [source, shell] .... # ifconfig wlan0 inet 192.168.0.2 netmask 255.255.255.0 ssid freebsdap # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.2 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet OFDM/54Mbps mode 11g status: associated ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode OPEN privacy OFF txpower 21.5 bmiss 7 scanvalid 60 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst .... [[network-wireless-ap-wpa]] ==== Punto de Acceso WPA2 basado en Host Esta sección se centra en configurar un punto de acceso FreeBSD usando el protocolo de seguridad WPA2. Se pueden encontrar más detalles acerca de WPA y de la configuración de clientes inalámbricos basados en WPA en <>. El demonio man:hostapd[8] se usa para manejar la autenticación del cliente y la gestión de la clave en el AP con WPA2 habilitado. Una máquina FreeBSD configurada como AP realiza las siguientes operaciones de configuración. Una vez que el AP está funcionando correctamente, se puede iniciar man:hostapd[8] durante el arranque con esta línea en [.filename]#/etc/rc.conf#: [.programlisting] .... hostapd_enable="YES" .... Antes de intentar configurar man:hostapd[8], primero configura los parámetros básicos presentados en <>. ===== WPA2-PSK WPA2-PSK está pensado para pequeñas redes donde no se puede o no es deseable utilizar un servidor de autenticación. La configuración se hace en [.filename]#/etc/hostapd.conf#: [.programlisting] .... interface=wlan0 <.> debug=1 <.> ctrl_interface=/var/run/hostapd <.> ctrl_interface_group=wheel <.> ssid=freebsdap <.> wpa=2 <.> wpa_passphrase=freebsdmall <.> wpa_key_mgmt=WPA-PSK <.> wpa_pairwise=CCMP <.> .... <.> Interfaz inalámbrica utilizada para el punto de acceso. <.> Nivel de verbosidad utilizado durante la ejecución de man:hostapd[8]. Un valor de `1` representa el nivel mínimo. <.> Ruta del directorio utilizado por man:hostapd[8] para almacenar ficheros de sockets de dominio para comunicación con programas externos como man:hostapd_cli[8]. En este ejemplo se usa el valor por defecto. <.> El grupo que tiene permitido el acceso a los ficheros de control del interfaz. <.> El nombre de la red inalámbrica, o SSID, que aparecerá en los escaneos inalámbricos. <.> Activa WPA y especifica qué protocolo de autenticación WPA se requerirá. Un valor de `2` configura el AP para WPA2 y es el recomendado. Establécelo a `1` sólo si se necesita el obsoleto WPA. <.> Contraseña ASCII para la autenticación WPA. <.> El protocolo de gestión de claves a usar. Este ejemplo establece WPA-PSK. <.> Algoritmos de encriptación aceptados por el punto de acceso. En este ejemplo, sólo se acepta el encriptador CCMP (AES). CCMP es una alternativa a TKIP y se prefiere siempre que se a posible. TKIP sólo debería permitirse cuando las estaciones con incapaces de usar CCMP. El siguiente paso es arrancar man:hostapd[8]: [source, shell] .... # service hostapd forcestart .... [source, shell] .... # ifconfig wlan0 wlan0: flags=8943 metric 0 mtu 1500 ether 04:f0:21:16:8e:10 inet6 fe80::6f0:21ff:fe16:8e10%wlan0 prefixlen 64 scopeid 0x9 nd6 options=21 media: IEEE 802.11 Wireless Ethernet autoselect mode 11na status: running ssid No5ignal channel 36 (5180 MHz 11a ht/40+) bssid 04:f0:21:16:8e:10 country US ecm authmode WPA2/802.11i privacy MIXED deftxkey 2 AES-CCM 2:128-bit AES-CCM 3:128-bit txpower 17 mcastrate 6 mgmtrate 6 scanvalid 60 ampdulimit 64k ampdudensity 8 shortgi wme burst dtimperiod 1 -dfs groups: wlan .... Una vez que el AP está funcionando, los clientes se pueden asociar a él. Consulta <> para más detalles. Es posible ver las estaciones asociadas con el AP usando `ifconfig _wlan0_ list sta`. [[network-usb-tethering]] == Tethering USB Muchos teléfonos móviles proporcionan la opción de compartir su conexión de datos a través de USB (habitualmente llamado "tethering"). Esta característica usa uno de los protocolos RNDIS, CDC o el protocolo personalizado Apple(R) iPhone(R)/iPad(R). * Los dispositivos Android(TM) normalmente utilizan el controlador man:urndis[4]. * Los dispositivos Apple(R) usan el controlador man:ipheth[4]. * Dispositivos más antiguos usarán habitualmente el controlador man:cdce[4]. Antes de conectar un dispositivo, carga el controlador apropiado en el kernel: [source, shell] .... # kldload if_urndis # kldload if_cdce # kldload if_ipheth .... Una vez que el dispositivo está conectado ``ue``_0_ estará disponible para que lo usemos como un dispositivo de red normal. Asegúrate de que el dispositivo tiene la opción "USB tethering" activada. Para hacer este cambio permanente y cargar el controlador como un módulo en el arranque, escribe la línea apropiada de las siguientes en [.filename]#/boot/loader.conf#: [source, shell] .... if_urndis_load="YES" if_cdce_load="YES" if_ipheth_load="YES" .... [[network-bluetooth]] == Bluetooth Bluetooth es una tecnología inalámbrica para crear redes personales que operen en la banda sin licenciar de 2.4 GHz, con un rago de 10 metros. Las redes se forman normalmente como ad-hoc a partir de dispositivos portátiles como teléfonos móviles, tabletas, y portátiles. A diferencia de la tecnología inalámbrica Wi-Fi, Bluetooth ofrece perfiles de servicio de más alto nivel, como servidores de fichero tipo FTP, envío de ficheros, transporte de voz, emulación de línea serie, y más. Esta sección describe el uso de un conector USB Bluetooth en un sistema FreeBSD. Después describe varias de las utilidades y protocolos de Bluetooth. === Cargando Soporte Bluetooth La pila Bluetooth en FreeBSD está implementada utilizando el framework man:netgraph[4]. Una gran variedad de conectores USB Bluetooth se soporta con man:ng_ubt[4]. Los dispositivos Bluetooth basados en Broadcom BCM2033 se soportan mediante los controladores man:ubtbcmfw[4] y man:ng_ubt[4]. La tarjeta 3Com Bluetooth PC Card 3CRWB60-A está soportada por el controlador man:ng_bt3c[4]. Los dispositivos serie y UART Bluetooth están soportados por man:sio[4], man:ng_h4[4], y man:hcseriald[8]. Antes de conectar un dispositivo, determina cuál de los controladores anteriores utiliza, después carga el controlador. Por ejemplo, si el dispositivo utiliza el controlador man:ng_ubt[4]: [source, shell] .... # kldload ng_ubt .... Si el dispositivo Bluetooth se va a conectar al sistema durante el arranque, éste se puede configurar para cargar el módulo durante el arranque añadiendo el controlador a [.filename]#/boot/loader.conf#: [.programlisting] .... ng_ubt_load="YES" .... Una vez que el controlador está cargado, conecta el dispositivo USB. Si el controlador se cargó de forma correcta, en la consola y en [.filename]#/var/log/messages# debería aparecer una salida similar a la siguiente: [source, shell] .... ubt0: vendor 0x0a12 product 0x0001, rev 1.10/5.25, addr 2 ubt0: Interface 0 endpoints: interrupt=0x81, bulk-in=0x82, bulk-out=0x2 ubt0: Interface 1 (alt.config 5) endpoints: isoc-in=0x83, isoc-out=0x3, wMaxPacketSize=49, nframes=6, buffer size=294 .... Para iniciar y parar la pila Bluetooth, utiliza su script de arranque. Es una buena idea parar la pila después de desconectar el dispositivo. Arrancar la pila bluetooth podría necesitar que se arranque man:hcsecd[8]. Cuando se arranca la pila, la salida debería ser similar a la siguiente: [source, shell] .... # service bluetooth start ubt0 BD_ADDR: 00:02:72:00:d4:1a Features: 0xff 0xff 0xf 00 00 00 00 00 <3-Slot> <5-Slot> Max. ACL packet size: 192 bytes Number of ACL packets: 8 Max. SCO packet size: 64 bytes Number of SCO packets: 8 .... === Encontrando Otros Dispositivos Bluetooth El Host Controller Interface (HCI) proporciona un método uniforme para acceder a las capacidades de la banda base de Bluetooth. En FreeBSD, se crea un nodo HCI de netgraph para cada dispositivo Bluetooth. Para más detalles, consulta man:ng_hci[4]. Una de las tareas más comunes es el descubrimiento de dispositivos Bluetooth dentro del rango de proximidad de RF. Esta operación se llama _inquiry_ (pregunta). Inquiry y otras operaciones HCI relacionadas se ejecutan con man:hccontrol[8]. El ejemplo de abajo muestra cómo encontrar dispositivos Bluetooth que están dentro de rango. La lista de dispositivos debería mostrarse en unos pocos segundos. Ten en cuenta que un dispositivo remoto sólo contestará a la pregunta si está en modo _descubrible_. [source, shell] .... % hccontrol -n ubt0hci inquiry Inquiry result, num_responses=1 Inquiry result #0 BD_ADDR: 00:80:37:29:19:a4 Page Scan Rep. Mode: 0x1 Page Scan Period Mode: 00 Page Scan Mode: 00 Class: 52:02:04 Clock offset: 0x78ef Inquiry complete. Status: No error [00] .... `BD_ADDR` es la dirección única de un dispositivo Bluetooth, similar a la dirección MAC de una tarjeta de red. Esta dirección es necesaria para la comunicación posterior con el dispositivo y es posible asignarle un valor que se amigable de leer. Hay información acerca de los hosts Bluetooth conocidos en [.filename]#/etc/bluetooth/hosts#. El siguiente ejemplo muestra cómo obtener un nombre legible por las personas que ha sido asignado a un dispositivo remoto: [source, shell] .... % hccontrol -n ubt0hci remote_name_request 00:80:37:29:19:a4 BD_ADDR: 00:80:37:29:19:a4 Name: Pav's T39 .... Si se realiza una pregunta a un dispositivo Bluetooth remoto, encontrará tu ordenador como "your.host.name (ubt0)". El nombre asignado al dispositivo local se puede cambiar en cualquier momento. Se puede asignar alias a los dispositivos remotos en [.filename]#/etc/bluetooth/hosts#. Se puede encontrar más información acerca de [.filename]#/etc/bluetooth/hosts# en man:bluetooth.hosts[5]. El sistema Bluetooth proporciona conexión punto a punto entre dos unidades Bluetooth, o punto a multipunto que se comparte entre varios dispositivos Bluetooth. El siguiente ejemplo muestra cómo crear una conexión con un dispositivo remoto: [source, shell] .... % hccontrol -n ubt0hci create_connection BT_ADDR .... `create_connection` acepta `BT_ADDR` así como los alias encontrados en [.filename]#/etc/bluetooth/hosts#. El siguiente ejemplo muestra cómo obtener la lista de conexiones activas en la banda base para el dispositivo local: [source, shell] .... % hccontrol -n ubt0hci read_connection_list Remote BD_ADDR Handle Type Mode Role Encrypt Pending Queue State 00:80:37:29:19:a4 41 ACL 0 MAST NONE 0 0 OPEN .... Un _manejador de conexión_ es útil cuando se requiere la terminación de una conexión de la banda base, aunque normalmente no es necesario hacer esto a mano. La pila terminará automáticamente las conexiones de banda base inactivas. [source, shell] .... # hccontrol -n ubt0hci disconnect 41 Connection handle: 41 Reason: Connection terminated by local host [0x16] .... Teclea `hccontrol help` para obtener un listado completo de los comandos HCI disponibles. La mayoría de los comandos HCI no requieren privilegios de súper usuario. === Emparejamiento de Dispositivos Por defecto, la comunicación Bluetooth no está autenticada, y cualquier dispositivo puede hablar con cualquier otro. Un dispositivo Bluetooth, como un teléfono móvil, podría decidir requerir autenticación para proporcionar un servicio particular. La autenticación Bluetooth se hacer normalmente con un _código PIN_, una cadena ASCII de hasta 16 caracteres. Se requiere que el usuario introduzca el mismo código PIN en ambos dispositivos. Una vez que el usuario ha introducido el código PIN, ambos dispositivos generarán una _clave de enlace_. Después de eso, la clave de enlace se puede almacenar en los dispositivos o en almacenamiento persistente. La siguiente vez, ambos dispositivos utilizarán las claves de enlace generadas previamente. Este procedimiento se llama _emparejamiento_. Ten en cuenta que si alguno de los dispositivos pierde la clave de enlace, se tiene que repetir el emparejamiento. El demonio man:hcsecd[8] es el responsable de manejar las peticiones de autenticación Bluetooth. El fichero de configuración por defecto es [.filename]#/etc/bluetooth/hcsecd.conf#. A continuación se muestra un ejemplo de sección para un teléfono móvil con el PIN establecido a `1234`: [.programlisting] .... device { bdaddr 00:80:37:29:19:a4; name "Pav's T39"; key nokey; pin "1234"; } .... La única limitación de los códigos PIN es la longitud. Algunos dispositivos, como los auriculares Bluetooth, podrían tener un código PIN fijo de fábrica. La opción `-d` fuerza a man:hcsecd[8] a permanecer en primer plano, de forma que es fácil ver lo que está pasando. Establece el dispositivo remoto para que reciba emparejamientos e inicia la conexión Bluetooth con el dispositivo remoto. El dispositivo remoto debería indicar que el emparejamiento ha sido aceptado y solicitar el código PIN. Introduce el mismo código PIN listado en [.filename]#hcsecd.conf#. Ahora el ordenador y el dispositivo remoto están emparejados. De forma alternativa, el emparejamiento puede ser iniciado por el dispositivo remoto. Se puede añadir la siguiente línea a [.filename]#/etc/rc.conf# para configurar que man:hcsecd[8] arranque automáticamente cuando se inicia el sistema: [.programlisting] .... hcsecd_enable="YES" .... Lo siguiente es una muestra de la salida del demonio man:hcsecd[8]: [.programlisting] .... hcsecd[16484]: Got Link_Key_Request event from 'ubt0hci', remote bdaddr 0:80:37:29:19:a4 hcsecd[16484]: Found matching entry, remote bdaddr 0:80:37:29:19:a4, name 'Pav's T39', link key doesn't exist hcsecd[16484]: Sending Link_Key_Negative_Reply to 'ubt0hci' for remote bdaddr 0:80:37:29:19:a4 hcsecd[16484]: Got PIN_Code_Request event from 'ubt0hci', remote bdaddr 0:80:37:29:19:a4 hcsecd[16484]: Found matching entry, remote bdaddr 0:80:37:29:19:a4, name 'Pav's T39', PIN code exists hcsecd[16484]: Sending PIN_Code_Reply to 'ubt0hci' for remote bdaddr 0:80:37:29:19:a4 .... === Acceso a la Red con Perfiles PPP Es posible utilizar un perfil DUN (Dial-Up Networking) para configurar un teléfono móvil como un módem inalámbrico para conectarse a un servidor de acceso a Internet. También se puede utilizar para configurar un ordenador para recibir llamadas de datos desde un teléfono móvil. Se puede usar el acceso a la red mediante un perfil PPP para proporcionar acceso LAN para uno o varios dispositivos Bluetooth. También puede proporcionar conexión PC a PC usando PPP sobre emulación de cable serie. En FreeBSD, estos perfiles se implementan con man:ppp[8] y el adaptador man:rfcomm_pppd[8] que convierte la conexión Bluetooth en algo que PPP puede usar. Antes de que se pueda usar el perfile, se debe crear una nueva etiqueta PPP en [.filename]#/etc/ppp/ppp.conf#. Consulta man:rfcomm_pppd[8] para ver ejemplos. En este ejemplo, se usa man:rfcomm_pppd[8] para abrir una conexión con un dispositivo remoto con un `BD_ADDR` de `00:80:37:29:19:a4` en un canal DUNRFCOMM: [source, shell] .... # rfcomm_pppd -a 00:80:37:29:19:a4 -c -C dun -l rfcomm-dialup .... El número de canal real se obtendrá del dispositivo remoto usando el protocolo SDP. Es posible especificar el canal RFCOMM a mano, y en este caso man:rfcomm_pppd[8] no realizará la consulta SDP. Usa man:sdpcontrol[8] para averiguar el canal RFCOMM en el dispositivo remoto. Para proporcionar acceso a la red con el servicio PPPLAN, se debe estar ejecutando man:sdpd[8] y se tienen que crear una nueva entrada para clientes LAN en [.filename]#/etc/ppp/ppp.conf#. Consulta man:rfcomm_pppd[8] para ver ejemplos. Finalmente, arrancar el servidor RFCOMMPPP en un número de canal RFCOMM válido. El servidor RFCOMMPPP registrará automáticamente el servicio LAN Bluetooth con el demonio SDP local. El ejemplo de abajo muestra cómo arrancar el servidor RFCOMMPPP. [source, shell] .... # rfcomm_pppd -s -C 7 -l rfcomm-server .... === Protocolos Bluetooth Esta sección proporciona un resumen de varios protocolos Bluetooth, sus funciones, y sus utilidades asociadas. ==== Logical Link Control and Adaptation Protocol (L2CAP) El Logical Link Control and Adaptation Protocol (L2CAP) proporciona servicios de datos orientados a conexión así como no orientados a conexión a los protocolos de las capas superiores. L2CAP permite a los protocolos y aplicaciones de niveles más altos transmitir y recibir paquetes de datos L2CAP de hasta 64 kilobytes de longitud. L2CAP se basa en el concepto de _canales_. Un canal es una conexión lógica construida sobre una conexión de banda base, donde cada canal está asociado a un sólo protocolo en una forma muchos-a-uno. Se pueden asociar múltiples canales al mismo protocolo, pero un canal no se puede asociar a múltiples protocolos. Cada paquete L2CAP recibido en un canal es redirigido al protocolo de nivel superior apropiado. Varios canales pueden compartir la misma conexión de banda base. En FreeBSD, se crear un nodo L2CAP de netgraph para cada dispositivo Bluetooth. Este nodo normalmente se conecta con el nodo HCI Bluetooth inferior y los nodos socket Bluetooth superiores. El nombre por defecto para el nodo L2CAP es "devicel2cap". Para más detalles consulta man:ng_l2cap[4]. Un comando útil es man:l2ping[8], que puede ser usado para hacer ping a otros dispositivos. Algunas implementaciones Bluetooth podrían no devolver todos los datos que se les envía, de forma que los `0 bytes` en el siguiente ejemplo es algo normal. [source, shell] .... # l2ping -a 00:80:37:29:19:a4 0 bytes from 0:80:37:29:19:a4 seq_no=0 time=48.633 ms result=0 0 bytes from 0:80:37:29:19:a4 seq_no=1 time=37.551 ms result=0 0 bytes from 0:80:37:29:19:a4 seq_no=2 time=28.324 ms result=0 0 bytes from 0:80:37:29:19:a4 seq_no=3 time=46.150 ms result=0 .... La utilidad man:l2control[8] se utiliza para realizar varias operaciones sobre los nodos L2CAP. Este ejemplo muestra cómo obtener la lista de conexiones lógicas (canales) y la lista de conexiones de banda base para el dispositivo local: [source, shell] .... % l2control -a 00:02:72:00:d4:1a read_channel_list L2CAP channels: Remote BD_ADDR SCID/ DCID PSM IMTU/ OMTU State 00:07:e0:00:0b:ca 66/ 64 3 132/ 672 OPEN % l2control -a 00:02:72:00:d4:1a read_connection_list L2CAP connections: Remote BD_ADDR Handle Flags Pending State 00:07:e0:00:0b:ca 41 O 0 OPEN .... Otra herramienta de diagnóstico es man:btsockstat[1]. Es similar a man:netstat[1], pero para estructuras de datos de red Bluetooth. El ejemplo de abajo muestra la misma conexión lógica como man:l2control[8] arriba. [source, shell] .... % btsockstat Active L2CAP sockets PCB Recv-Q Send-Q Local address/PSM Foreign address CID State c2afe900 0 0 00:02:72:00:d4:1a/3 00:07:e0:00:0b:ca 66 OPEN Active RFCOMM sessions L2PCB PCB Flag MTU Out-Q DLCs State c2afe900 c2b53380 1 127 0 Yes OPEN Active RFCOMM sockets PCB Recv-Q Send-Q Local address Foreign address Chan DLCI State c2e8bc80 0 250 00:02:72:00:d4:1a 00:07:e0:00:0b:ca 3 6 OPEN .... ==== Comunicación por Radio Frecuencia (RFCOMM) El protocolo RFCOMM proporciona emulación de puertos serie sobre el protocolo L2CAP. RFCOMM es un protocolo de transporte simple, con soporte adicional para emular los 9 circuitos de los puertos serie RS-2332 (EIATIA-232-E). Soporta hasta 60 conexiones simultáneas (canales RFCOMM) entre dos dispositivos Bluetooth. Para los propósitos de RFCOMM, un camino de comunicación completo incluye dos aplicaciones ejecutándose en los extremos de la comunicación con un segmento de comunicación entre ellos. RFCOMM está pensado para cubrir aplicaciones que hace uso de los puertos serie de los dispositivos en los que se encuentra. El segmento de comunicación es una enlace de conexión Bluetooth directa desde un dispositivo a otro. RFCOMM sólo se preocupa de la conexión entre los dispositivos en el caso de una conexión directa, o entre un dispositivo y un módem en el caso de red. RFCOMM puede soportar otras configuraciones, como módulos que se comunican vía tecnología inalámbrica Bluetooth en un lado y proporciona un interfaz por cable en el otro lado. En FreeBSD, RFCOMM está implementado en la capa de sockets Bluetooth. ==== Protocolo de Descubrimiento de Servicios (SDP) El Protocolo de Descubrimiento de Servicios (SDP) proporciona los medios para que las aplicaciones cliente descubran la existencia de servicios proporcionados por aplicaciones servidoras así como los atributos de dichos servicios. Los atributos de un servicio incluyen el tipo o clase del servicio ofrecido y el mecanismo o protocolo de información necesario para utilizarlo. SDP incluye comunicación entre un servidor SDP y un cliente SDP. El servidor mantiene una lista de registros de servicio que describe las características de los servicios asociados con el servidor. Cada registro de servicio contiene información acerca de un único servicio. Un cliente puede recuperar información de un registro de servicio mantenido por el servidor SDP realizando una petición SDP. Si el cliente, o una aplicación asociada con el cliente, decide usar un servicio, debe abrir una conexión separada con el proveedor del servicio para poder utilizarlo. SDP proporciona un mecanismo para descubrir servicios y sus atributos, pero no proporciona un mecanismo para utilizar esos servicios. Normalmente, un cliente SDP busca servicios basándose en alguna característica deseada de los servicios. Sin embargo, a veces es preferible descubrir qué tipos de servicios están descritos por los registros de servicio de un servidor SDP sin ninguna información previa acerca de los servicios. Este proceso de buscar cualquier servicio ofrecido se llama _navegación_ (browsing). El servidor SDP Bluetooth, man:sdpd[8], y cliente en línea de comando, man:sdpcontrol[8], están incluidos en la instalación estándar de FreeBSD. El siguiente ejemplo muestra cómo realizar una petición de navegación SDP. [source, shell] .... % sdpcontrol -a 00:01:03:fc:6e:ec browse Record Handle: 00000000 Service Class ID List: Service Discovery Server (0x1000) Protocol Descriptor List: L2CAP (0x0100) Protocol specific parameter #1: u/int/uuid16 1 Protocol specific parameter #2: u/int/uuid16 1 Record Handle: 0x00000001 Service Class ID List: Browse Group Descriptor (0x1001) Record Handle: 0x00000002 Service Class ID List: LAN Access Using PPP (0x1102) Protocol Descriptor List: L2CAP (0x0100) RFCOMM (0x0003) Protocol specific parameter #1: u/int8/bool 1 Bluetooth Profile Descriptor List: LAN Access Using PPP (0x1102) ver. 1.0 .... Ten en cuenta que cada servicio tiene una lista de atributos, como el canal RFCOMM. Dependiendo del servicio, el usuario podría necesitar anotar algunos de los atributos. Algunas implementaciones de Bluetooth no soportan la navegación de servicios y podrían devolver una lista vacía. En este caso, es posible buscar un servicio específico. El ejemplo inferior muestra cómo buscar el servicio OBEX Object Push (OPUSH): [source, shell] .... % sdpcontrol -a 00:01:03:fc:6e:ec search OPUSH .... Ofrecer servicios de FreeBSD a clientes Bluetooth se hace con el servidor man:sdpd[8]. Se puede añadir la siguiente línea a [.filename]#/etc/rc.conf#: [.programlisting] .... sdpd_enable="YES" .... El demonio man:sdpd[8] se puede arrancar con: [source, shell] .... # service sdpd start .... La aplicación servidora local que quiera proporcionar un servicio Bluetooth a clientes remotos registrará el servicio en el demonio SDP local. Un ejemplo de dicha aplicación es man:rfcomm_pppd[8]. Una vez iniciado, registrará el servicio LAN Bluetooth con el demonio local SDP. Se puede obtener la lista de servicios registrados en el servidor SDP local realizando una petición de navegación SDP mediante el canal de control local: [source, shell] .... # sdpcontrol -l browse .... ==== OBEX Object Push (OPUSH) Object Exchange (OBEX) es un protocolo ampliamente utilizado para transferencias de ficheros sencillas entre dispositivos móviles. Su principal uso está en la comunicación infrarroja, donde se usa para transferencias de ficheros genéricas entre portátiles o PDAs, y para enviar tarjetas de negocios o entradas de calendario entre teléfonos móviles y otros dispositivos con aplicaciones PIM (Personal Information Manager). El servidor y cliente OBEX están implementados por obexapp, que se pude instalar usando el paquete o port package:comms/obexapp[]. El cliente OBEX es utilizado para subir y/o bajar objetos del servidor OBEX. Un objeto de ejemplo es una tarjeta de negocio o una cita. El cliente OBEX puede obtener el número de canal RFCOMM del dispositivo remoto vía SDP. Esto se puede hacer especificando el nombre del servicio en lugar del número de canal RFCOMM. Los nombres de servicios soportados son: `IrMC`, `FTRN`, y `OPUSH`. También es posible especificar el canal RFCOMM como un número. Abajo hay un ejemplo de una sesión OBEX donde el objeto de información del dispositivo se descarga desde un teléfono móvil, y un nuevo objeto, la tarjeta de negocio, se sube al directorio del teléfono. [source, shell] .... % obexapp -a 00:80:37:29:19:a4 -C IrMC obex> get telecom/devinfo.txt devinfo-t39.txt Success, response: OK, Success (0x20) obex> put new.vcf Success, response: OK, Success (0x20) obex> di Success, response: OK, Success (0x20) .... Para proporcionar el servicio OPUSH, man:sdpd[8] debe estar en ejecución y se debe crear una carpeta raíz donde se almacenarán todos los objetos recibidos. La ruta por defecto de la carpeta raíz es [.filename]#/var/spool/obex#. Por último, inicia el servidor OBEX en un número de canal RFCOMM válido. El servidor OBEX registrará automáticamente el servicio OPUSH con el demonio SDP local. El ejemplo de abajo muestra cómo iniciar el servidor OBEX. [source, shell] .... # obexapp -s -C 10 .... ==== Perfil de Puerto Serie (SPP) El Perfil de Puerto Serie (SPP) permite a los dispositivos Bluetooth realizar emulación de cable serie. Este perfile permite a aplicaciones heredadas utilizar Bluetooth como un sustituto del cable, a través de una abstracción de puerto serie. En FreeBSD, man:rfcomm_sppd[1] implementa SPP y un pseudo tty es usado como abstracción de puerto serie virtual. El ejemplo de abajo muestra cómo conectarse al servicio de puerto serie de un dispositivo remoto. No se tiene que especificar un canal RFCOMM ya que man:rfcomm_sppd[1] puede obtenerlo del dispositivo remoto vía SDP. Para cambiar esto, especifica un canal RFCOMM en la línea de comando. [source, shell] .... # rfcomm_sppd -a 00:07:E0:00:0B:CA -t rfcomm_sppd[94692]: Starting on /dev/pts/6... /dev/pts/6 .... Una vez conectado, el pseudo tty puede ser usado como un puerto serie: [source, shell] .... # cu -l /dev/pts/6 .... El pseudo tty se imprime en stdout y se puede leer mediante scripts: [.programlisting] .... PTS=`rfcomm_sppd -a 00:07:E0:00:0B:CA -t` cu -l $PTS .... === Resolución de problemas Por defecto, cuando FreeBSD está aceptando una nueva conexión, intenta realizar un cambio de roles y convertirse en maestro. Algunos dispositivos Bluetooth más antiguos que no soportan el cambio de roles no serán capaces de conectar. Puesto que el cambio de roles se realiza cuando se establece una nueva conexión, no es posible preguntar al dispositivo remoto si soporta el cambio de roles. Sin embargo, hay una opción HCI para deshabilitar el intercambio de roles en el lado local: [source, shell] .... # hccontrol -n ubt0hci write_node_role_switch 0 .... Para mostrar paquetes Bluetooht, usa el paquete de terceros hcidump, que se puede instalar mediante el paquete o port package:comms/hcidump[]. Esta utilidad es similar a man:tcpdump[1] y se puede usar para mostrar el contenido de los paquetes Bluetooth en el terminal y para volcarlos a un fichero. [[network-bridging]] == Bridging A veces es útil dividir una red, como un segmento Ethernet, en segmentos de red sin tener que crear subredes IP y utilizar un router para conectar los segmentos entre ellos. Un dispositivo que conecta dos redes juntas de esta forma se llama "bridge". Un bridge funciona aprendiendo las direcciones MAC de los dispositivos en cada una de sus interfaces de red. Reenvía tráfico entre las redes sólo cuando las direcciones de origen y destino están en diferentes redes. En muchos aspectos, un bridge es como un switch Ethernet con muy pocos puertos. Un sistema FreeBSD como varias interfaces de red puede ser configurado para funcionar como un bridge. Un bridge puede ser útil en las siguientes situaciones: Conectar Redes:: La operación básica de un bridge es juntar dos o más segmentos de red. Hay muchas razones para usar un bridge basado en host en lugar de un equipamiento de red, como restricciones en el cableado o los firewalls. Un bridge también puede conectar una interfaz inalámbrica funcionando en modo hostap con una red cableada y actuar como punto de acceso. Filtrado/Firewall the Modelado de Tráfico:: Se puede usar un bridge cuando se necesita funcionalidad de firewall sin enrutado o traducciones de direcciones de red (NAT, Network Address Translation). + Un ejemplo es una pequeña compañía que está conectada a un ISP mediante DSL o ISDN. Hay trece direcciones IP públicas del ISP y diez ordenadores en la red. En esta situación, usar un firewall basado en un router es difícil por problemas con las subredes. Un firewall basado en bridge se puede configurar sin ningún problema con las direcciones IP. Network Tap:: Un bridge puede unir dos segmentos de red para inspeccionar todas las tramas Ethernet que pasan entre ellos usando man:bpf[4] y man:tcpdump[1] en la interfaz bridge, o enviando una copia de todas las tramas hacia un interfaz adicional conocido como un puerto span. VPN en la Capa 2:: Dos redes Ethernet se pueden unir mediante un enlace IP uniendo las redes a un túnel EtherIP o a una solución basada en man:tap[4] como OpenVPN. Redundancia en la Capa 2:: Una red puede estar conectada con múltiples enlaces y usar el Spanning Tree Protocol (STP) para bloquear caminos redundantes. Esta sección describe cómo configurar un sistema FreeBSD como un bridge usando man:if_bridge[4]. También hay disponible un driver bridge de netgraph, y se describe en man:ng_bridge[4]. [NOTE] ==== Se pude usar filtrado de paquetes con cualquier paquete de firewall que se enganche en el framework man:pfil[9]. El bridge se puede usar como un perfilador de tráfico con man:altq[4] o man:dummynet[4]. ==== === Habilitando el Bridge En FreeBSD, man:if_bridge[4] es un módulo del kernel que se carga automáticamente cuando man:ifconfig[8] crea un interfaz bridge. También es posible compilar soporte para bridge en un kernel personalizado añadiendo `device if_bridge` al fichero de configuración del kernel personalizado. El bridge se crea clonando una interfaz. Para crear la interfaz bridge: [source, shell] .... # ifconfig bridge create bridge0 # ifconfig bridge0 bridge0: flags=8802 metric 0 mtu 1500 ether 96:3d:4b:f1:79:7a id 00:00:00:00:00:00 priority 32768 hellotime 2 fwddelay 15 maxage 20 holdcnt 6 proto rstp maxaddr 100 timeout 1200 root id 00:00:00:00:00:00 priority 0 ifcost 0 port 0 .... Cuando se crea una interfaz bridge, se le asigna automáticamente una dirección Ethernet generada de forma aleatoria. Los parámetros `maxaddr` y `timeout` controlan cuántas direcciones MAC puede mantener el bridge en su tabla de reenvío y cuántos segundos deben pasar para eliminarla desde la última vez que han sido vistas. Los otros parámetros controlan cómo opera STP. Después, especifica qué interfaces de red añadir como miembros del bridge. Para que el bridge sea capaz de reenviar paquetes, todas las interfaces y el bridge necesitan estar levantadas: [source, shell] .... # ifconfig bridge0 addm fxp0 addm fxp1 up # ifconfig fxp0 up # ifconfig fxp1 up .... El puente ahora puede reenviar tramas Ethernet entre [.filename]#fxp0# y [.filename]#fxp1#. Añade las siguientes líneas a [.filename]#/etc/rc.conf# de forma que el bridge se cree al arrancar: [.programlisting] .... cloned_interfaces="bridge0" ifconfig_bridge0="addm fxp0 addm fxp1 up" ifconfig_fxp0="up" ifconfig_fxp1="up" .... Si la máquina bridge necesita una dirección IP, establécela en la interfaz del bridge, no en las interfaces que son miembro. La dirección puede establecerse estáticamente o vía DHCP. Este ejemplo establece una dirección IP estática: [source, shell] .... # ifconfig bridge0 inet 192.168.0.1/24 .... También es posible establecer una dirección IPv6 al interfaz del bridge. Para hacer los cambios permanentes, añade la información de la dirección a [.filename]#/etc/rc.conf#. [NOTE] ==== Cuando el filtrado de paquetes está habilitado, los paquetes que van por el bridge pasarán a través del filtro de entrada en la interfaz de origen en el interfaz del bridge, y de salida en las interfaces apropiadas. Cualquiera de las dos fases puede deshabilitarse. Cuando la dirección de un paquete es importante, es mejor aplicar el firewall en las interfaces que forman el bridge que en el bridge en sí mismo. El bridge tiene varios valores configurables para pasar paquetes IP y no IP, y firewall the capa 2 con man:ipfw[8]. Consulta man:if_bridge[4] para más información. ==== === Activando Spanning Tree Para que una red Ethernet funcione adecuadamente, sólo debe existir un camino activo entre dos dispositivos. El protocolo STP detecta bucles y pone enlaces redundantes en un estado bloqueado. Si uno de los enlaces activos fallara, STP calcula un árbol diferente y activa uno de los caminos bloqueados para restaurar la conectividad a todos los puntos de la red. El protocolo Rapid Spanning Tree (RSTP o 802.1w) proporciona compatibilidad hacia atrás con el STP antiguo. RSTP proporciona una convergencia más rápida e intercambia información con switches vecinos para transicionar rápidamente a modo reenvío sin crear bycles. FreeBSD soporta como modos de operación RSTP y STP, siendo RSTP el modo por defecto. Se puede activar STP en las interfaces miembro usando man:ifconfig[8]. Para un bridge con [.filename]#fxp0# y [.filename]#fxp1# como interfaces actuales, activa STP con: [source, shell] .... # ifconfig bridge0 stp fxp0 stp fxp1 bridge0: flags=8843 metric 0 mtu 1500 ether d6:cf:d5:a0:94:6d id 00:01:02:4b:d4:50 priority 32768 hellotime 2 fwddelay 15 maxage 20 holdcnt 6 proto rstp maxaddr 100 timeout 1200 root id 00:01:02:4b:d4:50 priority 32768 ifcost 0 port 0 member: fxp0 flags=1c7 port 3 priority 128 path cost 200000 proto rstp role designated state forwarding member: fxp1 flags=1c7 port 4 priority 128 path cost 200000 proto rstp role designated state forwarding .... Este bridge tiene un spanning tree con un ID de `00:01:02:4b:d4:50` y una prioridad de `32768`. Como el `root id` es el mismo, eso indica que es el bridge raíz del árbol. Otro bridge en la red tiene STP activado: [source, shell] .... bridge0: flags=8843 metric 0 mtu 1500 ether 96:3d:4b:f1:79:7a id 00:13:d4:9a:06:7a priority 32768 hellotime 2 fwddelay 15 maxage 20 holdcnt 6 proto rstp maxaddr 100 timeout 1200 root id 00:01:02:4b:d4:50 priority 32768 ifcost 400000 port 4 member: fxp0 flags=1c7 port 4 priority 128 path cost 200000 proto rstp role root state forwarding member: fxp1 flags=1c7 port 5 priority 128 path cost 200000 proto rstp role designated state forwarding .... La línea `root id 00:01:02:4b:d4:50 priority 32768 ifcost 400000 port 4` muestra que el bridge raíz es `00:01:02:4b:d4:50` y que tiene un camino con coste `400000` desde este bridge. La ruta al brige raíz es vía `port 4` que es [.filename]#fxp0#. === Parámetros de la Interfaz del Bridge Varios parámetros de `ifconfig` son únicos de las interfaces del bridge. Esta sección resume algunos casos comunes para estos parámetros. man:ifconfig[8] describe la lista completa de parámetros disponibles. private:: Una interfaz privada no reenvía nada de tráfico a otro puerto que no esté designado como una interfaz privada. El tráfico se bloquea incondicionalmente de forma que las tramas Ethernet no serán reenviadas, incluyendo los paquetes ARP. Si se necesita bloquear el tráfico de forma selectiva, se tiene que usar un firewall. span:: Un puerto span transmite una copia de cada trama Ethernet recibida en el bridge. El número de puertos span configurados en el bridge es ilimitado, pero si una interfaz es designada como un puerto span, no puede ser usada también como un puerto regular en el bridge. Esto es muy útil para husmear en una red con bridge de forma pasiva en otro host conectado a uno de los puertos span del bridge. Por ejemplo, para enviar una copia de todas las tramas obtenidas de la interfaz [.filename]#fxp4#: + [source, shell] .... # ifconfig bridge0 span fxp4 .... sticky:: Si una interfaz del bridge es marcada como sticky, las entradas de direcciones aprendidas dinámicamente se tratan como entradas estáticas en la caché de reenvío. Las entradas sticky no envejecen nunca en la caché ni son reemplazadas, incluso si la dirección es vista en otra interfaz. Esto ofrece el beneficio de las entradas de direcciones estáticas sin la necesidad de poblar la tabla de reenvío con antelación. Los clientes que se han aprendido de un segmento del bridge en particular no pueden moverse a otro segmento. + Un ejemplo de uso de direcciones sticky es combinar el bridge con VLANs para aislar redes cliente sin gastar espacio de direcciones IP. Considera que `CustomerA` está en `vlan100`, `CustomerB` está en `vlan101`, y el bridge tiene la dirección `192.168.0.1`: + [source, shell] .... # ifconfig bridge0 addm vlan100 sticky vlan100 addm vlan101 sticky vlan101 # ifconfig bridge0 inet 192.168.0.1/24 .... + En este ejemplo, ambos clientes ven `192.168.0.1` como su gateway por defecto. Puesto que la caché del bridge es sticky, un host no puede falsear la dirección MAC de otro cliente para interceptar su tráfico. + Cualquier comunicación entre las VLANs se puede bloquear utilizando un firewall o, como se ve en este ejemplo, usando interfaces privadas: + [source, shell] .... # ifconfig bridge0 private vlan100 private vlan101 .... + Los clientes están completamente aislados entre sí y el rango de direcciones completo `/24` se puede reservar sin necesidad de crear subredes. + Se puede limitar el número direcciones MAC fuente únicas detrás de una interfaz. Una vez que se alcance el límite, los paquetes que tengan una dirección de origen desconocida serán descartados hasta que una entrada existente de caché en el host que expire o que sea eliminada. + El siguiente ejemplo establece el número máximo de dispositivos Ethernet a 10 para `CustomerA` en `vlan100`: + [source, shell] .... # ifconfig bridge0 ifmaxaddr vlan100 10 .... Las interfaces del bridge también soportan modo monitor, donde los paquetes son descartados después de haber sido procesados por man:bpf[4] y no se procesan más ni se reenvían. Esto se puede usar para multiplexar la entrada de dos o más interfaces en un único flujo man:bpf[4]. Esto es útil para reconstruir el tráfico de redes que transmiten las señales RX/TX hacia fuera usando dos interfaces separadas. Por ejemplo, para leer la entrada desde cuatro interfaces de red como un único flujo: [source, shell] .... # ifconfig bridge0 addm fxp0 addm fxp1 addm fxp2 addm fxp3 monitor up # tcpdump -i bridge0 .... === Monitorización SNMP El interfaz bridge y los parámetros STP se pueden monitorizar con man:bsnmpd[1] que se incluye con el sistema base FreeBSD. Las MIBs del bridge exportadas siguen el estándar IETF de forma que se puede usar cualquier cliente SNMP o paquete de monitorización para recuperar los datos. Para habilitar la monitorización en el bridge, descomenta esta línea en [.filename]#/etc/snmpd.config# eliminando el símbolo `+#+` al comienzo: [.programlisting] .... begemotSnmpdModulePath."bridge" = "/usr/lib/snmp_bridge.so" .... Podría ser necesario modificar en este fichero otros valores de configuración, como los nombres de la comunidad y listas de acceso. Consulta man:bsnmpd[1] y man:snmp_bridge[3]. Una vez guardados los cambios, añade esta línea a [.filename]#/etc/rc.conf#: [.programlisting] .... bsnmpd_enable="YES" .... Después, arranca man:bsnmpd[1]: [source, shell] .... # service bsnmpd start .... Los siguientes ejemplos usan el software Net-SNMP (package:net-mgmt/net-snmp[]) para consultar un bridge desde un sistema cliente. También se puede usar el port package:net-mgmt/bsnmptools[]. Desde el cliente SNMP que está ejecutando Net-SNMP, añade las siguientes líneas a [.filename]#$HOME/.snmp/snmp.conf# para importar las definiciones MIB del bridge: [.programlisting] .... mibdirs +/usr/share/snmp/mibs mibs +BRIDGE-MIB:RSTP-MIB:BEGEMOT-MIB:BEGEMOT-BRIDGE-MIB .... Para monitorizar un sólo bridge usando IETF BRIDGE-MIB (RFC4188): [source, shell] .... % snmpwalk -v 2c -c public bridge1.example.com mib-2.dot1dBridge BRIDGE-MIB::dot1dBaseBridgeAddress.0 = STRING: 66:fb:9b:6e:5c:44 BRIDGE-MIB::dot1dBaseNumPorts.0 = INTEGER: 1 ports BRIDGE-MIB::dot1dStpTimeSinceTopologyChange.0 = Timeticks: (189959) 0:31:39.59 centi-seconds BRIDGE-MIB::dot1dStpTopChanges.0 = Counter32: 2 BRIDGE-MIB::dot1dStpDesignatedRoot.0 = Hex-STRING: 80 00 00 01 02 4B D4 50 ... BRIDGE-MIB::dot1dStpPortState.3 = INTEGER: forwarding(5) BRIDGE-MIB::dot1dStpPortEnable.3 = INTEGER: enabled(1) BRIDGE-MIB::dot1dStpPortPathCost.3 = INTEGER: 200000 BRIDGE-MIB::dot1dStpPortDesignatedRoot.3 = Hex-STRING: 80 00 00 01 02 4B D4 50 BRIDGE-MIB::dot1dStpPortDesignatedCost.3 = INTEGER: 0 BRIDGE-MIB::dot1dStpPortDesignatedBridge.3 = Hex-STRING: 80 00 00 01 02 4B D4 50 BRIDGE-MIB::dot1dStpPortDesignatedPort.3 = Hex-STRING: 03 80 BRIDGE-MIB::dot1dStpPortForwardTransitions.3 = Counter32: 1 RSTP-MIB::dot1dStpVersion.0 = INTEGER: rstp(2) .... El valor `dot1dStpTopChanges.0` es dos, lo que indica que la topología STP ha cambiado dos veces. Un cambio de topología significa que uno o más enlaces en la red han cambiado o fallado y se ha tenido que calcular un nuevo árbol. El valor `dot1dStpTimeSinceTopologyChange.0` mostrará cuándo sucede esto. Para monitorizar múltiples interfaces del bridge, se puede usar el BEGEMOT-BRIDGE-MIB privado: [source, shell] .... % snmpwalk -v 2c -c public bridge1.example.com enterprises.fokus.begemot.begemotBridge BEGEMOT-BRIDGE-MIB::begemotBridgeBaseName."bridge0" = STRING: bridge0 BEGEMOT-BRIDGE-MIB::begemotBridgeBaseName."bridge2" = STRING: bridge2 BEGEMOT-BRIDGE-MIB::begemotBridgeBaseAddress."bridge0" = STRING: e:ce:3b:5a:9e:13 BEGEMOT-BRIDGE-MIB::begemotBridgeBaseAddress."bridge2" = STRING: 12:5e:4d:74:d:fc BEGEMOT-BRIDGE-MIB::begemotBridgeBaseNumPorts."bridge0" = INTEGER: 1 BEGEMOT-BRIDGE-MIB::begemotBridgeBaseNumPorts."bridge2" = INTEGER: 1 ... BEGEMOT-BRIDGE-MIB::begemotBridgeStpTimeSinceTopologyChange."bridge0" = Timeticks: (116927) 0:19:29.27 centi-seconds BEGEMOT-BRIDGE-MIB::begemotBridgeStpTimeSinceTopologyChange."bridge2" = Timeticks: (82773) 0:13:47.73 centi-seconds BEGEMOT-BRIDGE-MIB::begemotBridgeStpTopChanges."bridge0" = Counter32: 1 BEGEMOT-BRIDGE-MIB::begemotBridgeStpTopChanges."bridge2" = Counter32: 1 BEGEMOT-BRIDGE-MIB::begemotBridgeStpDesignatedRoot."bridge0" = Hex-STRING: 80 00 00 40 95 30 5E 31 BEGEMOT-BRIDGE-MIB::begemotBridgeStpDesignatedRoot."bridge2" = Hex-STRING: 80 00 00 50 8B B8 C6 A9 .... Para cambiar la interfaz del bridge que está siendo monitorizada mediante el subárbol `mib-2.dot1dBridge`: [source, shell] .... % snmpset -v 2c -c private bridge1.example.com BEGEMOT-BRIDGE-MIB::begemotBridgeDefaultBridgeIf.0 s bridge2 .... [[network-aggregation]] == Agregación de Enlaces y Conmutación FreeBSD proporciona la interfaz man:lagg[4] que se puede usar para agregar múltiples interfaces de red en una interfaz virtual para proporcionar tolerancia a fallos ("failover") y agregación de enlaces. El failover permite que el tráfico continúe fluyendo mientras haya al menos una interfaz de red que tenga establecido un enlace. La agregación de enlaces funciona mejor en switches que soportan LACP, ya que este protocolo distribuye el tráfico de forma bidireccional a la vez que responde al fallo de enlaces individuales. Los protocolos de agregación soportados por el interfaz lagg determinan qué puertos se usan para tráfico saliente y si un puerto específico acepta o no tráfico de entrada. Los siguientes protocolos están soportados por man:lagg[4]: failover:: Este modo envía y recibe tráfico sólo a través del puerto maestro. Si el puerto maestro no está disponible, se usa el siguiente puerto activo. La primera interfaz añadida a la interfaz virtual es el puerto maestro y todas las interfaces añadidas posteriormente se usan como dispositivos redundantes. Si se produce un cambio a un puerto no maestro, el puerto original se convierte en maestro una vez que esté disponible de nuevo. loadbalance:: Esto proporciona una configuración estática y no negocia agregación con los pares o intercambia marcos para monitorizar el enlace. Si el switch soporta LACP, se debería usar en su lugar. lacp:: El protocolo IEEE(R) 802.3ad Link Aggregation Control Protocol (LACP) negocia un conjunto de enlaces agregables con el par en uno o más grupos "Link Aggregated Groups" (LAGs). Cada LAG se compone de puertos con la misma velocidad, conjunto de operaciones full-duplex, y el tráfico se balancea entre los puertos en el LAG con la velocidad total mayor. Típicamente, sólo hay un LAG que contiene todos los puertos. En el caso de cambios en la conectividad física, LACP convergerá rápido a una nueva configuración. + LACP balancea el tráfico de salida a lo largo de los puestos activos basándose en un hash de la cabecera de información del protocolo y acepta tráfico de entrada de cualquier puerto activo. El hash incluye la fuente Ehternet y la dirección de destino y, si está disponible, la etiqueta VLAN, y las direcciones de fuente y destino IPv4 o IPv6. roundrobin:: Este modo distribuye el tráfico de salida utilizando un planificador round-robin entre todos los puertos activos y acepta tráfico de entrada desde cualquier puerto activo. Puesto que esto viola el orden de las tramas Ethernet, debería ser usado con precaución. broadcast:: Este modo envía tráfico de salida a todos los puertos configurados en la interfaz lagg, y recibe tramas desde cualquier puerto. === Ejemplos de Configuración Esta sección muestra cómo configurar un switch Cisco(R) y un sistema FreeBSD para hacer balanceado de carga LACP. Después muestra cómo configurar dos interfaces Ethernet en modo failover así como cómo configurar el modo failover entre una interfaz Ethernet y otra inalámbrica. [[networking-lacp-aggregation-cisco]] .Agregación LACP con un Switch Cisco(R) [example] ==== Este ejemplo conecta dos interfaces Ethernet man:fcp[4] en una máquina FreeBSD con los dos primeros puertos Ethernet en un switch Cisco(R) como un enlace único de balanceo de carga y tolerante a fallos. Se pueden añadir más interfaces para incrementar la productividad y la tolerancia a fallos. Reemplaza los nombres de los puertos Cisco(R), dispositivos Ethernet, número de grupo del canal, y dirección IP como se muestra en el ejemplo para adaptarlo a la configuración local. El orden de las tramas es obligatorio en los enlaces Ethernet y cualquier tráfico entre dos estaciones siempre debe fluir por el mismo enlace físico, limitando la velocidad máxima a aquella de un interfaz. El algoritmo de transmisión intenta usar la mayor cantidad de información posible para distinguir entre distintos flujos de tráfico y balancear los flujos entre las interfaces disponibles. En el switch Cisco(R), añade las interfaces _FastEthernet0/1_ y _FastEthernet0/2_ al grupo del canal _1_: [source, shell] .... interface FastEthernet0/1 channel-group 1 mode active channel-protocol lacp ! interface FastEthernet0/2 channel-group 1 mode active channel-protocol lacp .... En el sistema FreeBSD, crea el interfaz man:lagg[4] usando las interfaces físicas _fxp0_ y _fxp1_ y levanta las interfaces con la dirección IP _10.0.0.3/24_: [source, shell] .... # ifconfig fxp0 up # ifconfig fxp1 up # ifconfig lagg0 create # ifconfig lagg0 up laggproto lacp laggport fxp0 laggport fxp1 10.0.0.3/24 .... Después, verifica el estado de la interfaz virtual: [source, shell] .... # ifconfig lagg0 lagg0: flags=8843 metric 0 mtu 1500 options=8 ether 00:05:5d:71:8d:b8 inet 10.0.0.3 netmask 0xffffff00 broadcast 10.0.0.255 media: Ethernet autoselect status: active laggproto lacp laggport: fxp1 flags=1c laggport: fxp0 flags=1c .... Los puertos marcados como `ACTIVE` forman parte del LAG que se ha negociado con el switch remoto. El tráfico será transmitido y recibido a través de estos puertos activos. Añade `-v` al comando de arriba para ver los identificadores LAG. Para ver el estado del puerto en el switch Cisco(R): [source, shell] .... switch# show lacp neighbor Flags: S - Device is requesting Slow LACPDUs F - Device is requesting Fast LACPDUs A - Device is in Active mode P - Device is in Passive mode Channel group 1 neighbors Partner's information: LACP port Oper Port Port Port Flags Priority Dev ID Age Key Number State Fa0/1 SA 32768 0005.5d71.8db8 29s 0x146 0x3 0x3D Fa0/2 SA 32768 0005.5d71.8db8 29s 0x146 0x4 0x3D .... Para más detalles, teclea `show lacp neighbor detail`. Para mantener esta configuración entre reinicios, añade las siguientes entradas en el fichero [.filename]#/etc/rc.conf#del sistema FreeBSD: [.programlisting] .... ifconfig_fxp0="up" ifconfig_fxp1="up" cloned_interfaces="lagg0" ifconfig_lagg0="laggproto lacp laggport fxp0 laggport fxp1 10.0.0.3/24" .... ==== [[networking-lagg-failover]] .Modo Failover [example] ==== El modo failover se puede usar para cambiar a un interfaz secundario si se pierde el enlace en el interfaz maestro. Para configurar failover, asegúrate de que las interfaces físicas están levantadas, después crea el interfaz man:lagg[4]. En este ejemplo, _fxp0_ es la interfaz maestra, _fxp1_ es la interfaz secundaria, y el interfaz virtual tiene asignada la dirección IP _10.0.0.15/24_: [source, shell] .... # ifconfig fxp0 up # ifconfig fxp1 up # ifconfig lagg0 create # ifconfig lagg0 up laggproto failover laggport fxp0 laggport fxp1 10.0.0.15/24 .... La interfaz virtual debería tener un aspecto parecido a este: [source, shell] .... # ifconfig lagg0 lagg0: flags=8843 metric 0 mtu 1500 options=8 ether 00:05:5d:71:8d:b8 inet 10.0.0.15 netmask 0xffffff00 broadcast 10.0.0.255 media: Ethernet autoselect status: active laggproto failover laggport: fxp1 flags=0<> laggport: fxp0 flags=5 .... El tráfico será transmitido y recibido en _fxp0_. Si se pierde el enlace en _fxp0_, _fxp1_ se convertirá en el enlace activo. Si se restaura el enlace en la interfaz maestra, se convertirá de nuevo en el enlace activo. Para mantener esta configuración entre reinicios, añade las siguientes entradas en [.filename]#/etc/rc.conf#: [.programlisting] .... ifconfig_fxp0="up" ifconfig_fxp1="up" cloned_interfaces="lagg0" ifconfig_lagg0="laggproto failover laggport fxp0 laggport fxp1 10.0.0.15/24" .... ==== [[networking-lagg-wired-and-wireless]] .Modo Failover Entre Interfaces Ethernet y Wireless [example] ==== Para los usuarios de portátiles, normalmente es deseable configurar el dispositivo inalámbrico como un secundario que sólo usa cuando la conexión Ethernet no está disponible. Con man:lagg[4], es posible configurar un failover que prefiera la conexión Ethernet tanto por rendimiento como por razones de seguridad, mientras que se mantiene la posibilidad de transferir datos por la conexión inalámbrica. Esto se consigue sobrescribiendo la dirección MAC del interfaz Ethernet con el de la interfaz inalámbrica. [NOTE] -**** +==== En teoría, cualquiera de las dos direcciones MAC (Ethernet o inalámbrica) se puede cambiar para igualarse a la otra. Sin embargo, algunas interfaces inalámbricas populares carecen del soporte para sobrescribir la dirección MAX. Por lo tanto para este propósito recomendamos sobrescribir la dirección MAC Ethernet. -**** +==== [NOTE] -**** +==== Si el controlador para el interfaz inalámbrico no está cargado en el kernel `GENERIC` o en el personalizado, y el ordenador está ejecutando FreeBSD{rel121-current}, carga el [.filename]#.ko# correspondiente en [.filename]#/boot/loader.conf# añadiendo `*driver_load="YES"*` a ese fichero y después reiniciando. Otra forma mejor es cargar el driver en [.filename]#/etc/rc.conf# añadiéndolo a `kld_list` (consulta man:rc.conf[5] para los detalles) en ese fichero y reiniciando. Esto es necesario porque de otra forma el controlador no está todavía cargado en el momento en el que se configura el interfaz man:lagg[4]. -**** +==== En este ejemplo, el interfaz Ethernet, _re0_, es el maestro y el interfaz inalámbrico, _wlan0_, es el recambio. El interfaz _wlan0_ ha sido creado a partir del interfaz inalámbrico físico _ath0_, y el interfaz Ethernet se configurará con la dirección MAC del interfaz inalámbrico. Primero, levanta el interfaz inalámbrico (reemplaza _FR_ con tu código de país de dos letras), pero no establezcas una dirección IP. Reemplaza _wlan0_ con el nombre del interfaz inalámbrico del sistema: [source, shell] .... # ifconfig wlan0 create wlandev ath0 country FR ssid my_router up .... Ahora puedes saber la dirección MAC del interfaz inalámbrico: [source, shell] .... # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether b8:ee:65:5b:32:59 groups: wlan ssid Bbox-A3BD2403 channel 6 (2437 MHz 11g ht/20) bssid 00:37:b7:56:4b:60 regdomain ETSI country FR indoor ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 2:128-bit txpower 30 bmiss 7 scanvalid 60 protmode CTS ampdulimit 64k ampdudensity 8 shortgi -stbctx stbcrx -ldpc wme burst roaming MANUAL media: IEEE 802.11 Wireless Ethernet MCS mode 11ng status: associated nd6 options=29 .... La línea `ether` contendrá la dirección MAC del interfaz especificado. Ahora, cambia la dirección MAC del interfaz Ethernet para que concuerde: [source, shell] .... # ifconfig re0 ether b8:ee:65:5b:32:59 .... Asegúrate de que el interfaz _re0_ está levantado, luego crea el interfaz man:lagg[4] con _re0_ como maestro con _wlan0_ como recambio: [source, shell] .... # ifconfig re0 up # ifconfig lagg0 create # ifconfig lagg0 up laggproto failover laggport re0 laggport wlan0 .... La interfaz virtual debería tener un aspecto parecido a este: [source, shell] .... # ifconfig lagg0 lagg0: flags=8843 metric 0 mtu 1500 options=8 ether b8:ee:65:5b:32:59 laggproto failover lagghash l2,l3,l4 laggport: re0 flags=5 laggport: wlan0 flags=0<> groups: lagg media: Ethernet autoselect status: active .... Después, arranca el cliente DHCP para obtener una dirección IP: [source, shell] .... # dhclient lagg0 .... Para mantener esta configuración entre reinicios, añade las siguientes entradas en [.filename]#/etc/rc.conf#: [.programlisting] .... ifconfig_re0="ether b8:ee:65:5b:32:59" wlans_ath0="wlan0" ifconfig_wlan0="WPA" create_args_wlan0="country FR" cloned_interfaces="lagg0" ifconfig_lagg0="up laggproto failover laggport re0 laggport wlan0 DHCP" .... ==== [[network-diskless]] == Operación sin Disco con PXE El Preboot eXecution Environment (PXE) de Intel(R) permite a un sistema operativo arrancar por red. Por ejemplo, un sistema FreeBSD puede arrancar sobre la red y operar sin un disco local, usando sistemas de ficheros montados desde un servidor NFS. El soporte de PXE normalmente está disponible en la BIOS. Para usar PXE cuando arranca la máquina, selecciona la opción `Arrancar desde la red` en la configuración de la BIOS o teclea una tecla de función durante la inicialización del sistema. Para proporcionar a un sistema operativo los ficheros necesarios para que arranque sobre la red, la configuración de PXE también necesita configurar apropiadamente DHCP, TFTP y los servidores NFS, donde: * Parámetros iniciales, como una dirección IP, el nombre del fichero ejecutable de arranque y su localización, el nombre del servidor, y la ruta raíz se obtienen del servidor DHCP. * El fichero del cargador del sistema operativo se obtiene mediante TFTP. * Los sistemas de ficheros se cargan usando NFS. Cuando un ordenador arranca mediante PXE, recibe información a través de DHCP sobre dónde obtener el fichero inicial del cargador de arranque. Después de que el ordenador recibe esta información, descarga el cargador de arranque vía TFTP y después ejecuta el cargador de arranque. En FreeBSD, el fichero del cargador de arranque es [.filename]#/boot/pxeboot#. Después de que [.filename]#/boot/pxeboot# se ejecute, se carga el kernel de FreeBSD y el resto de la secuencia de arranque de FreeBSD prosigue su curso como se describe en crossref:boot[boot,El Proceso de Arranque de FreeBSD]. Esta sección describe cómo configurar estos servicios en un sistema FreeBSD de forma que otros sistemas puedan arrancar mediante PXE en FreeBSD. Consulta man:diskless[8] para más información. [CAUTION] ==== Como se ha descrito, el sistema que proporciona estos servicios no es seguro. Debería vivir en un área protegida de la red y otros hosts no deberían confiar en él. ==== [[network-pxe-nfs]] === Configurando el Entorno PXE Las pasos que se muestran en esta sección configuran los servidores NFS y TFTP incluidos. La siguiente sección muestra cómo instalar y configurar el servidor DHCP. En este ejemplo, el dirección que contendrá los ficheros usados por los usuarios PXE es [.filename]#/b/tftpboot/FreeBSD/install#. Es importante que este directorio exista y que el nombre del directorio esté configurado tanto en [.filename]#/etc/inetd.conf# como en [.filename]#/usr/local/etc/dhcpd.conf#. [NOTE] ==== Los ejemplos de comandos que siguen asumen el uso del shell man:sh[1]. Los usuarios de man:chs[1] y man:tcsh[1] tendrán que iniciar un shell man:sh[1] o adaptar los comandos a la sintaxis de man:csh[1]. ==== [.procedure] . Crea el directorio raíz que contendrá la instalación de FreeBSD que será montada por NFS: + [source, shell] .... # export NFSROOTDIR=/b/tftpboot/FreeBSD/install # mkdir -p ${NFSROOTDIR} .... . Activa el servidor NFS añadiendo esta línea a [.filename]#/etc/rc.conf#: + [.programlisting] .... nfs_server_enable="YES" .... . Exporta el directorio raíz del sistema sin disco vía NFS añadiendo lo siguiente a [.filename]#/etc/exports#: + [.programlisting] .... /b -ro -alldirs -maproot=root .... . Arranca el servidor NFS: + [source, shell] .... # service nfsd start .... . Activa man:inetd[8] añadiendo la siguiente línea a [.filename]#/etc/rc.conf#: + [.programlisting] .... inetd_enable="YES" .... . Descomenta la siguiente línea en [.filename]#/etc/inetd.conf# asegurándote de que no comienza con un símbolo `+#+`: + [.programlisting] .... tftp dgram udp wait root /usr/libexec/tftpd tftpd -l -s /b/tftpboot .... + [NOTE] ==== Algunas versiones de PXE necesitan la versión TCP de TFTP. En este caso, descomenta la segunda línea `tftp` que contiene `stream tcp`. ==== . Arranca man:inetd[8]: + [source, shell] .... # service inetd start .... . Instala el sistema base en [.filename]#${NFSROOTDIR}#, bien descomprimiendo los archivos oficiales o recompilando el kernel de FreeBSD y el espacio de usuario (consulta crossref:cutting-edge[makeworld,“Actualizando FreeBSD desde las Fuentes”] para instrucciones más detalladas, pero no olvides añadir `DESTDIR=_${NFSROOTDIR}_` cuando ejecutes los comands `make installkernel` y `make installworld`. . Comprueba que el servidor TFTP funciona y que puede descargar el cargador de arranque que se obtendrá vía PXE: + [source, shell] .... # tftp localhost tftp> get FreeBSD/install/boot/pxeboot Received 264951 bytes in 0.1 seconds .... . Edita [.filename]#${NFSROOTDIR}/etc/fstab# y crea una entrada para montar el sistema de ficheros raíz sobre NFS: + [.programlisting] .... # Device Mountpoint FSType Options Dump Pass myhost.example.com:/b/tftpboot/FreeBSD/install / nfs ro 0 0 .... + Reemplaza _myhost.example.com_ con el nombre de la máquina o la dirección IP del servidor NFS. En este ejemplo, el sistema de ficheros raíz está montado como solo lectura para evitar que los clientes NFS puedan borrar los contenidos del sistema de ficheros raíz. . Establece la contraseña de root en el entorno PXE para las máquinas cliente que están arrancando mediante PXE: + [source, shell] .... # chroot ${NFSROOTDIR} # passwd .... . Si es necesario, habilita el inicio de sesión de root en man:ssh[1] para las máquinas cliente que arrancan con PXE editando [.filename]#${NFSROOTDIR}/etc/ssh/sshd_config# y habilitando `PermitRootLogin`. Esta opción está documentada en man:sshd_config[5]. . Realiza cualquier otra personalización del entorno PXE en [.filename]#${NFSROOTDIR}#. Estas personalizaciones podrían incluir cosas como instalación de paquetes o editar el fichero de contraseñas con man:vipw[8]. Cuando se arranca desde un volumen raíz NFS, [.filename]#/etc/rc# detecta el arranque NFS y ejecuta [.filename]#/etc/rc.initdiskless#. En este caso se necesita que [.filename]#/etc# y [.filename]#/var# estén cargados den memoria de forma que estos directorios sean escribibles pero el directorio raíz NFS sea de sólo escritura: [source, shell] .... # chroot ${NFSROOTDIR} # mkdir -p conf/base # tar -c -v -f conf/base/etc.cpio.gz --format cpio --gzip etc # tar -c -v -f conf/base/var.cpio.gz --format cpio --gzip var .... Cuando el sistema arranca, los sistemas de fichero en memoria para [.filename]#/etc# y [.filename]#/var# se crearán y montarán y el contenido de los ficheros [.filename]#cpio.gz# se copiará dentro de ellos. Por defecto, estos sistemas de ficheros tienen una capacidad máxima de 5 megabytes. Si tus archivos no caben, que es habitualmente el caso para [.filename]#/var# cuando se han instalado paquetes binarios, solicita más tamaño poniendo el número de sectores de 512 bytes necesarios (por ejemplo, 5 megabytes es 10240 sectores) en [.filename]#${NFSROOTDIR}/conf/base/etc/md_size# y [.filename]#${NFSROOTDIR}/conf/base/var/md_size# para los sistemas de ficheros [.filename]#/etc# y [.filename]#/var# respectivamente. [[network-pxe-setting-up-dhcp]] === Configurando el Servidor DHCP El servidor DHCP no necesita estar en la misma máquina que los servidores de TFTP y NFS, pero necesita estar accesible en la red. DHCP no es parte del sistema base de FreeBSD pero se puede instalar usando el port o paquete package:net/isc-dhcp44-server[]. Una vez instalado, edita el fichero de configuración, [.filename]#/usr/local/etc/dhcpd.conf#. Configura las opciones `next-server`, `filename`, y `root-path` como se ve en este ejemplo: [.programlisting] .... subnet 192.168.0.0 netmask 255.255.255.0 { range 192.168.0.2 192.168.0.3 ; option subnet-mask 255.255.255.0 ; option routers 192.168.0.1 ; option broadcast-address 192.168.0.255 ; option domain-name-servers 192.168.35.35, 192.168.35.36 ; option domain-name "example.com"; # IP address of TFTP server next-server 192.168.0.1 ; # path of boot loader obtained via tftp filename "FreeBSD/install/boot/pxeboot" ; # pxeboot boot loader will try to NFS mount this directory for root FS option root-path "192.168.0.1:/b/tftpboot/FreeBSD/install/" ; } .... La directiva `next-server` se usa para especificar la dirección IP del servidor TFTP. La directiva `filename` define la ruta a [.filename]#/boot/pxeboot#. Se usa un nombre de fichero relativo, lo que significa que [.filename]#/b/tftpboot# no está incluido en la ruta. La opción `root-path` define la ruta al sistema de ficheros raíz NFS. Una vez salvados los cambios, activa DHCP durante el arranque añadiendo la siguiente línea a [.filename]#/etc/rc.conf#: [.programlisting] .... dhcpd_enable="YES" .... Después inicia el servicio DHCP: [source, shell] .... # service isc-dhcpd start .... === Depurando problemas en PXE Una vez que todos los servicios están configurados y arrancados, los clientes PXE deberían ser capaces de cargar automáticamente FreeBSD a través de la red. Si un cliente particular no es capaz de conectar, cuando ese cliente arranque, entra en el menú de configuración de la BIO y confirma que está configurado para arrancar desde la red. Esta sección describe algunos consejos para resolución de problemas para aislar la fuente del problema de configuración si los clientes no fueran capaces de arrancar mediante PXE. [.procedure] **** . Usa el paquete o port package:net/wireshark[] para depurar el tráfico de red involucrado en el proceso de arranque de PXE, el cual se ilustra en el diagrama inferior. + .Proceso de Arranque de PXE con Punto de Montaje Raíz NFS image::pxe-nfs.png[] + 1. El cliente emite un mensaje DHCPDISCOVER. + 2. El servidor DHCP responde con los valores para la dirección IP, next-server, filename y root-path. + 3. El cliente envía una solicitud TFTP a next-server, pidiendo recuperar un nombre de archivo. + 4. El servidor TFTP responde y envía el fichero al cliente. + 5. El cliente ejecuta el fichero, que es pxeboot(8), que luego carga el kernel. Cuando el kernel se ejecuta, el sistema de ficheros raíz especificado por root-path es montado a través de NFS. + . En el servidor TFTP, lee [.filename]#/var/log/xferlog# para asegurar que se está recuperando [.filename]#pxeboot# desde el lugar correcto. Prueba con este ejemplo de configuración: + [source, shell] .... # tftp 192.168.0.1 tftp> get FreeBSD/install/boot/pxeboot Received 264951 bytes in 0.1 seconds .... + La sección `BUGS` en man:tftpd[8] y man:tftp[1] documentan algunas limitaciones con TFTP. . Asegúrate de que el sistema de ficheros raíz se puede montar vía NFS. Puedes probar con esta configuración de ejemplo: + [source, shell] .... # mount -t nfs 192.168.0.1:/b/tftpboot/FreeBSD/install /mnt .... **** [[carp]] == Common Address Redundancy Protocol (CARP) El protocolo CARP (Common Address Redundancy Protocol) permite a múltiples hosts compartir la misma dirección IP y VHID (Virtual Host ID) para proporcionar _alta disponibilidad_ para uno o más servicios. Este significa que uno o más hosts pueden fallar, y los otros hosts se harán cargo de forma transparente de forma que los usuarios no verán un fallo de servicio. Además de la dirección IP compartida, cada host tiene su propia dirección IP para gestión y configuración. Todas las máquinas que comparten una dirección IP tienen el mismo VHID. El VHID para cada dirección IP virtual debe ser única en el dominio broadcast del interfaz de red. La alta disponibilidad con CARP está incluida en FreeBSD, aunque los pasos para configurarla varían ligeramente dependiendo de la versión de FreeBSD. Esta sección proporciona la misma configuración de ejemplo para versiones anteriores, iguales o posteriores a FreeBSD 10. Este ejemplo configura soporte para failover con tres hosts, todos con una única dirección IP, pero proporcionando el mismo contenido web. Tiene dos maestros diferentes llamados `hosta.example.org` y `hostb.example.org`, con un respaldo compartido llamado `hostc.example.org`. Estas máquinas están balanceadas con un DNS con figurado en Round Robin . Las máquinas maestro y el respaldo están configuradas de forma idéntica excepto por los nombres de host y las direcciones IP de gestión. Estos servidores deben tener la misma configuración y ejecutar los mismos servicios. Cuando se produce un failover, las peticiones al servicio en la dirección IP compartida sólo pueden ser contestadas correctamente si el servidor de respaldo tiene acceso al mismo contenido. La máquina de respaldo tiene dos interfaces CARP adicionales, una para cada dirección IP de los servidores maestros. Cuando ocurre un fallo, el servidor de respaldo pillará la dirección IP de la máquina del maestro que haya fallado. [[carp-10x]] === Usando CARP en FreeBSD 10 y Posterior Activa el soporte de CARP en el arranque añadiendo una entrada para el módulo del kernel [.filename]#carp.ko# en [.filename]#/boot/loader.conf#: [.programlisting] .... carp_load="YES" .... Para cargar el módulo ahora sin reiniciar: [source, shell] .... # kldload carp .... Para los usuarios que prefieren usar un kernel personalizado, incluye la siguiente línea en el fichero de configuración del kernel personalizado y compila como se describe en crossref:kernelconfig[kernelconfig,Configurando el Kernel de FreeBSD]: [.programlisting] .... device carp .... El nombre de host, dirección IP de gestión y su máscara de subred, la dirección IP compartida, y el VHID se configuran añadiendo entradas en [.filename]#/etc/rc.conf#. Este ejemplo es para `hosta.example.org`: [.programlisting] .... hostname="hosta.example.org" ifconfig_em0="inet 192.168.1.3 netmask 255.255.255.0" ifconfig_em0_alias0="inet vhid 1 pass testpass alias 192.168.1.50/32" .... El siguiente conjunto de entradas es para `hostb.example.org`. Puesto que representa un segundo maestro, utiliza una dirección IP compartida y VHID diferentes. Sin embargo, la contraseña especificada con `pass` debe ser idéntica ya que CARP sólo escuchará y aceptará notificaciones de las máquinas que tengan la contraseña correcta. [.programlisting] .... hostname="hostb.example.org" ifconfig_em0="inet 192.168.1.4 netmask 255.255.255.0" ifconfig_em0_alias0="inet vhid 2 pass testpass alias 192.168.1.51/32" .... La tercer máquina, `hostc.example.org`, está configurada para manejar el failover de cualquiera de los maestros. Esta máquina está configurada con dos CARPVHIDS, uno para manejar cada dirección IP virtual de cada host maestro. El desvío de notificaciones CARP, `advskew`, está configurado para asegurar que el host de respaldo notifica más tarde que el maestro, puesto que `advskew` controla el orden de precedencia cuando hay varios servidores de reemplazo. [.programlisting] .... hostname="hostc.example.org" ifconfig_em0="inet 192.168.1.5 netmask 255.255.255.0" ifconfig_em0_alias0="inet vhid 1 advskew 100 pass testpass alias 192.168.1.50/32" ifconfig_em0_alias1="inet vhid 2 advskew 100 pass testpass alias 192.168.1.51/32" .... Tener dos CARPVHIDs configurados significa que `hostc.example.org` se dará cuenta si alguno de los maestros no se encuentra disponible. Si un maestro no es capaz de notificar antes que el servidor de reemplazo, el servidor de reemplazo usará la dirección IP compartida hasta que el maestro esté disponible de nuevo. [NOTE] ==== Si el maestro original vuelve a estar disponible, `hostc.example.org` no liberará la dirección IP virtual automáticamente. Para que esto suceda, se tiene que habilitar la preemptividad. Esto está deshabilitado por defecto, está controlado mediante la variable `net.inet.carp.preempt` de man:sysctl[8]. El administrador puede forzar a que el servidor de reemplazo devuelva la dirección IP al maestro: [source, shell] .... # ifconfig em0 vhid 1 state backup .... ==== Una vez completada la configuración, reinicia la red o reinicia cada sistema. Ahora la alta disponibilidad está habilitada. La funcionalidad CARP se puede controlar mediante varias variables de man:sysctl[8] que están documentadas en las páginas de manual man:carp[4]. Se pueden disparar otras acciones a partir de eventos CARP usando man:devd[8]. [[carp-9x]] === Usando CARP en FreeBSD 9 y Anteriores La configuración para estas versiones de FreeBSD es similar a la descrita en la sección previa, excepto que se tiene que crear primero un dispositivo CARP y hacer referencia a él en la configuración. Activa el soporte de CARP al arrancar cargando el módulo del kernel [.filename]#if_carp.ko# en [.filename]#/boot/loader.conf#: [.programlisting] .... if_carp_load="YES" .... Para cargar el módulo ahora sin reiniciar: [source, shell] .... # kldload carp .... Para los usuarios que prefieren usar un kernel personalizado, incluye la siguiente línea en el fichero de configuración del kernel personalizado y compila como se describe en crossref:kernelconfig[kernelconfig,Configurando el Kernel de FreeBSD]: [.programlisting] .... device carp .... Después, en cada host, crea un dispositivo CARP: [source, shell] .... # ifconfig carp0 create .... Establece el nombre de host, dirección IP de gestión, dirección IP compartida, y VHID añadiendo las líneas necesarias a [.filename]#/etc/rc.conf#. Puesto que se usa un dispositivo CARP virtual en lugar de un alias, se usa la máscara de subred `/24` en lugar de `/32`. Aquí están las entradas para `hosta.example.org`: [.programlisting] .... hostname="hosta.example.org" ifconfig_fxp0="inet 192.168.1.3 netmask 255.255.255.0" cloned_interfaces="carp0" ifconfig_carp0="vhid 1 pass testpass 192.168.1.50/24" .... En `hostb.example.org`: [.programlisting] .... hostname="hostb.example.org" ifconfig_fxp0="inet 192.168.1.4 netmask 255.255.255.0" cloned_interfaces="carp0" ifconfig_carp0="vhid 2 pass testpass 192.168.1.51/24" .... La tercera máquina, `hostc.example.org`, se configura para manejar el failover de cualquiera de los hosts maestros: [.programlisting] .... hostname="hostc.example.org" ifconfig_fxp0="inet 192.168.1.5 netmask 255.255.255.0" cloned_interfaces="carp0 carp1" ifconfig_carp0="vhid 1 advskew 100 pass testpass 192.168.1.50/24" ifconfig_carp1="vhid 2 advskew 100 pass testpass 192.168.1.51/24" .... [NOTE] ==== La preemptividad está deshabilitada en el kernel [.filename]#GENERIC# de FreeBSD. Si la preemptividad se ha habilitado con un kernel personalizado `hostc.example.org` podría no devolver la dirección IP al servidor original. El administrador puede forzar que el servidor de reemplazo devuelve al dirección IP al maestro con el comando: [source, shell] .... # ifconfig carp0 down && ifconfig carp0 up .... Esto se debería hacer en la interfaz [.filename]#carp# que se corresponda con el host correcto. ==== Una vez completada la configuración, reinicia la red o reinicia cada sistema. Ahora la alta disponibilidad está habilitada. [[network-vlan]] == VLANs VLANs son una forma de dividir una red de forma virtual en muchas subredes diferentes, también llamado segmentación. Cada segmento tendrá su dominio broadcast y está aislado de otras VLANs. En FreeBSD, las VLANs tienen que estar soportadas por el controlador de la tarjeta de red. Para ver qué controladores soportan vlans, consulta la página de manual man:vlan[4]. Cuando se configura una VLAN, se tienen que conocer un par de datos. Primero, ¿qué interfaz de red? Segundo, ¿cuál es la etiqueta de la VLAN? Para configurar una VLAN en tiempo de ejecución, con un NIC de `em0` y una etiqueta de VLAN de `5` el comando se parecería a este: [source, shell] .... # ifconfig em0.5 create vlan 5 vlandev em0 inet 192.168.20.20/24 .... [NOTE] ==== ¿Te has fijado en cómo el nombre de la interfaz incluye el nombre del controlador del NIC y la etiqueta VLAN, separados por un punto? Esta es la mejor forma de mantener la configuración de la VLAN sencilla cuando hay muchas VLANs en la máquina. ==== Para configurar VLANs en el arranque, se tiene que actualizar [.filename]#/etc/rc.conf#. Para duplicar la configuración de arriba, se tiene que añadir lo siguiente: [.programlisting] .... vlans_em0="5" ifconfig_em0_5="inet 192.168.20.20/24" .... Se pueden añadir VLANs adicionales, simplemente añadiendo la etiqueta al campo `vlans_em0` y añadiendo una línea adicional configurando la red en la interfaz de esa etiqueta VLAN. Es útil asignar un nombre simbólico a una interfaz de forma que cuando el hardware asociado cambie, sólo se necesiten actualizar unas pocas variables de configuración. Por ejemplo, las cámaras de seguridad necesitan ejecutarse sobre VLAN 1 en `em0`. Después, si la tarjeta `em0` es reemplazada con una tarjeta que utiliza el controlador man:ixgb[4], no habrá que cambiar a `ixgb0.1` todas las referencias a `em0.1`. Para configurar la VLAN `5`, en el NIC `em0`, asigna el nombre de interfaz `cameras`, y asignar al interfaz la dirección IP `_192.168.20.20_` con un prefijo de `24` bits, usa este comando: [source, shell] .... # ifconfig em0.5 create vlan 5 vlandev em0 name cameras inet 192.168.20.20/24 .... Para un interfaz llamado `video`, usa lo siguiente: [source, shell] .... # ifconfig video.5 create vlan 5 vlandev video name cameras inet 192.168.20.20/24 .... Para aplicar los cambios en el arranque, añade las siguientes líneas a [.filename]#/etc/rc.conf#: [.programlisting] .... vlans_video="cameras" create_args_cameras="vlan 5" ifconfig_cameras="inet 192.168.20.20/24" .... diff --git a/documentation/content/pl/books/handbook/advanced-networking/_index.adoc b/documentation/content/pl/books/handbook/advanced-networking/_index.adoc index dc8b9310d8..b8c0f6d09c 100644 --- a/documentation/content/pl/books/handbook/advanced-networking/_index.adoc +++ b/documentation/content/pl/books/handbook/advanced-networking/_index.adoc @@ -1,2821 +1,2821 @@ --- title: Rozdział 32. Advanced Networking part: Część IV. Komunikacja sieciowa prev: books/handbook/firewalls next: books/handbook/partv showBookMenu: true weight: 37 params: path: "/books/handbook/advanced-networking/" --- [[advanced-networking]] = Advanced Networking :doctype: book :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumoffset: 32 :partnums: :source-highlighter: rouge :experimental: :images-path: books/handbook/advanced-networking/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] [[advanced-networking-synopsis]] == Synopsis This chapter covers a number of advanced networking topics. After reading this chapter, you will know: * The basics of gateways and routes. * How to set up USB tethering. * How to set up IEEE(R) 802.11 and Bluetooth(R) devices. * How to make FreeBSD act as a bridge. * How to set up network PXE booting. * How to set up IPv6 on a FreeBSD machine. * How to enable and utilize the features of the Common Address Redundancy Protocol (CARP) in FreeBSD. * How to configure multiple VLANs on FreeBSD. * Configure bluetooth headset. Before reading this chapter, you should: * Understand the basics of the [.filename]#/etc/rc# scripts. * Be familiar with basic network terminology. * Know how to configure and install a new FreeBSD kernel (crossref:kernelconfig[kernelconfig,Configuring the FreeBSD Kernel]). * Know how to install additional third-party software (crossref:ports[ports,Installing Applications: Packages and Ports]). [[network-routing]] == Gateways and Routes _Routing_ is the mechanism that allows a system to find the network path to another system. A _route_ is a defined pair of addresses which represent the "destination" and a "gateway". The route indicates that when trying to get to the specified destination, send the packets through the specified gateway. There are three types of destinations: individual hosts, subnets, and "default". The "default route" is used if no other routes apply. There are also three types of gateways: individual hosts, interfaces, also called links, and Ethernet hardware (MAC) addresses. Known routes are stored in a routing table. This section provides an overview of routing basics. It then demonstrates how to configure a FreeBSD system as a router and offers some troubleshooting tips. [[network-routing-default]] === Routing Basics To view the routing table of a FreeBSD system, use man:netstat[1]: [source,shell] .... % netstat -r Routing tables Internet: Destination Gateway Flags Refs Use Netif Expire default outside-gw UGS 37 418 em0 localhost localhost UH 0 181 lo0 test0 0:e0:b5:36:cf:4f UHLW 5 63288 re0 77 10.20.30.255 link#1 UHLW 1 2421 example.com link#1 UC 0 0 host1 0:e0:a8:37:8:1e UHLW 3 4601 lo0 host2 0:e0:a8:37:8:1e UHLW 0 5 lo0 => host2.example.com link#1 UC 0 0 224 link#1 UC 0 0 .... The entries in this example are as follows: default:: The first route in this table specifies the `default` route. When the local system needs to make a connection to a remote host, it checks the routing table to determine if a known path exists. If the remote host matches an entry in the table, the system checks to see if it can connect using the interface specified in that entry. + If the destination does not match an entry, or if all known paths fail, the system uses the entry for the default route. For hosts on a local area network, the `Gateway` field in the default route is set to the system which has a direct connection to the Internet. When reading this entry, verify that the `Flags` column indicates that the gateway is usable (`UG`). + The default route for a machine which itself is functioning as the gateway to the outside world will be the gateway machine at the Internet Service Provider (ISP). localhost:: The second route is the `localhost` route. The interface specified in the `Netif` column for `localhost` is [.filename]#lo0#, also known as the loopback device. This indicates that all traffic for this destination should be internal, rather than sending it out over the network. MAC address:: The addresses beginning with `0:e0:` are MAC addresses. FreeBSD will automatically identify any hosts, `test0` in the example, on the local Ethernet and add a route for that host over the Ethernet interface, [.filename]#re0#. This type of route has a timeout, seen in the `Expire` column, which is used if the host does not respond in a specific amount of time. When this happens, the route to this host will be automatically deleted. These hosts are identified using the Routing Information Protocol (RIP), which calculates routes to local hosts based upon a shortest path determination. subnet:: FreeBSD will automatically add subnet routes for the local subnet. In this example, `10.20.30.255` is the broadcast address for the subnet `10.20.30` and `example.com` is the domain name associated with that subnet. The designation `link#1` refers to the first Ethernet card in the machine. + Local network hosts and local subnets have their routes automatically configured by a daemon called man:routed[8]. If it is not running, only routes which are statically defined by the administrator will exist. host:: The `host1` line refers to the host by its Ethernet address. Since it is the sending host, FreeBSD knows to use the loopback interface ([.filename]#lo0#) rather than the Ethernet interface. + The two `host2` lines represent aliases which were created using man:ifconfig[8]. The `=>` symbol after the [.filename]#lo0# interface says that an alias has been set in addition to the loopback address. Such routes only show up on the host that supports the alias and all other hosts on the local network will have a `link#1` line for such routes. 224:: The final line (destination subnet `224`) deals with multicasting. Various attributes of each route can be seen in the `Flags` column. <> summarizes some of these flags and their meanings: [[routeflags]] .Commonly Seen Routing Table Flags [cols="1,1", frame="none", options="header"] |=== | Command | Purpose |U |The route is active (up). |H |The route destination is a single host. |G |Send anything for this destination on to this gateway, which will figure out from there where to send it. |S |This route was statically configured. |C |Clones a new route based upon this route for machines to connect to. This type of route is normally used for local networks. |W |The route was auto-configured based upon a local area network (clone) route. |L |Route involves references to Ethernet (link) hardware. |=== On a FreeBSD system, the default route can defined in [.filename]#/etc/rc.conf# by specifying the IP address of the default gateway: [.programlisting] .... defaultrouter="10.20.30.1" .... It is also possible to manually add the route using `route`: [source,shell] .... # route add default 10.20.30.1 .... Note that manually added routes will not survive a reboot. For more information on manual manipulation of network routing tables, refer to man:route[8]. [[network-static-routes]] === Configuring a Router with Static Routes A FreeBSD system can be configured as the default gateway, or router, for a network if it is a dual-homed system. A dual-homed system is a host which resides on at least two different networks. Typically, each network is connected to a separate network interface, though IP aliasing can be used to bind multiple addresses, each on a different subnet, to one physical interface. In order for the system to forward packets between interfaces, FreeBSD must be configured as a router. Internet standards and good engineering practice prevent the FreeBSD Project from enabling this feature by default, but it can be configured to start at boot by adding this line to [.filename]#/etc/rc.conf#: [.programlisting] .... gateway_enable="YES" # Set to YES if this host will be a gateway .... To enable routing now, set the man:sysctl[8] variable `net.inet.ip.forwarding` to `1`. To stop routing, reset this variable to `0`. The routing table of a router needs additional routes so it knows how to reach other networks. Routes can be either added manually using static routes or routes can be automatically learned using a routing protocol. Static routes are appropriate for small networks and this section describes how to add a static routing entry for a small network. [NOTE] ==== For large networks, static routes quickly become unscalable. FreeBSD comes with the standard BSD routing daemon man:routed[8], which provides the routing protocols RIP, versions 1 and 2, and IRDP. Support for the BGP and OSPF routing protocols can be installed using the package:net/zebra[] package or port. ==== Consider the following network: image::static-routes.png[] In this scenario, `RouterA` is a FreeBSD machine that is acting as a router to the rest of the Internet. It has a default route set to `10.0.0.1` which allows it to connect with the outside world. `RouterB` is already configured to use `192.168.1.1` as its default gateway. Before adding any static routes, the routing table on `RouterA` looks like this: [source,shell] .... % netstat -nr Routing tables Internet: Destination Gateway Flags Refs Use Netif Expire default 10.0.0.1 UGS 0 49378 xl0 127.0.0.1 127.0.0.1 UH 0 6 lo0 10.0.0.0/24 link#1 UC 0 0 xl0 192.168.1.0/24 link#2 UC 0 0 xl1 .... With the current routing table, `RouterA` does not have a route to the `192.168.2.0/24` network. The following command adds the `Internal Net 2` network to ``RouterA``'s routing table using `192.168.1.2` as the next hop: [source,shell] .... # route add -net 192.168.2.0/24 192.168.1.2 .... Now, `RouterA` can reach any host on the `192.168.2.0/24` network. However, the routing information will not persist if the FreeBSD system reboots. If a static route needs to be persistent, add it to [.filename]#/etc/rc.conf#: [.programlisting] .... # Add Internal Net 2 as a persistent static route static_routes="internalnet2" route_internalnet2="-net 192.168.2.0/24 192.168.1.2" .... The `static_routes` configuration variable is a list of strings separated by a space, where each string references a route name. The variable `route_internalnet2` contains the static route for that route name. Using more than one string in `static_routes` creates multiple static routes. The following shows an example of adding static routes for the `192.168.0.0/24` and `192.168.1.0/24` networks: [.programlisting] .... static_routes="net1 net2" route_net1="-net 192.168.0.0/24 192.168.0.1" route_net2="-net 192.168.1.0/24 192.168.1.1" .... [[network-routing-troubleshooting]] === Troubleshooting When an address space is assigned to a network, the service provider configures their routing tables so that all traffic for the network will be sent to the link for the site. But how do external sites know to send their packets to the network's ISP? There is a system that keeps track of all assigned address spaces and defines their point of connection to the Internet backbone, or the main trunk lines that carry Internet traffic across the country and around the world. Each backbone machine has a copy of a master set of tables, which direct traffic for a particular network to a specific backbone carrier, and from there down the chain of service providers until it reaches a particular network. It is the task of the service provider to advertise to the backbone sites that they are the point of connection, and thus the path inward, for a site. This is known as route propagation. Sometimes, there is a problem with route propagation and some sites are unable to connect. Perhaps the most useful command for trying to figure out where routing is breaking down is `traceroute`. It is useful when `ping` fails. When using `traceroute`, include the address of the remote host to connect to. The output will show the gateway hosts along the path of the attempt, eventually either reaching the target host, or terminating because of a lack of connection. For more information, refer to man:traceroute[8]. [[network-routing-multicast]] === Multicast Considerations FreeBSD natively supports both multicast applications and multicast routing. Multicast applications do not require any special configuration in order to run on FreeBSD. Support for multicast routing requires that the following option be compiled into a custom kernel: [.programlisting] .... options MROUTING .... The multicast routing daemon, mrouted can be installed using the package:net/mrouted[] package or port. This daemon implements the DVMRP multicast routing protocol and is configured by editing [.filename]#/usr/local/etc/mrouted.conf# in order to set up the tunnels and DVMRP. The installation of mrouted also installs map-mbone and mrinfo, as well as their associated man pages. Refer to these for configuration examples. [NOTE] ==== DVMRP has largely been replaced by the PIM protocol in many multicast installations. Refer to man:pim[4] for more information. ==== [[network-wireless]] == Wireless Networking === Wireless Networking Basics Most wireless networks are based on the IEEE(R) 802.11 standards. A basic wireless network consists of multiple stations communicating with radios that broadcast in either the 2.4GHz or 5GHz band, though this varies according to the locale and is also changing to enable communication in the 2.3GHz and 4.9GHz ranges. 802.11 networks are organized in two ways. In _infrastructure mode_, one station acts as a master with all the other stations associating to it, the network is known as a BSS, and the master station is termed an access point (AP). In a BSS, all communication passes through the AP; even when one station wants to communicate with another wireless station, messages must go through the AP. In the second form of network, there is no master and stations communicate directly. This form of network is termed an IBSS and is commonly known as an _ad-hoc network_. 802.11 networks were first deployed in the 2.4GHz band using protocols defined by the IEEE(R) 802.11 and 802.11b standard. These specifications include the operating frequencies and the MAC layer characteristics, including framing and transmission rates, as communication can occur at various rates. Later, the 802.11a standard defined operation in the 5GHz band, including different signaling mechanisms and higher transmission rates. Still later, the 802.11g standard defined the use of 802.11a signaling and transmission mechanisms in the 2.4GHz band in such a way as to be backwards compatible with 802.11b networks. Separate from the underlying transmission techniques, 802.11 networks have a variety of security mechanisms. The original 802.11 specifications defined a simple security protocol called WEP. This protocol uses a fixed pre-shared key and the RC4 cryptographic cipher to encode data transmitted on a network. Stations must all agree on the fixed key in order to communicate. This scheme was shown to be easily broken and is now rarely used except to discourage transient users from joining networks. Current security practice is given by the IEEE(R) 802.11i specification that defines new cryptographic ciphers and an additional protocol to authenticate stations to an access point and exchange keys for data communication. Cryptographic keys are periodically refreshed and there are mechanisms for detecting and countering intrusion attempts. Another security protocol specification commonly used in wireless networks is termed WPA, which was a precursor to 802.11i. WPA specifies a subset of the requirements found in 802.11i and is designed for implementation on legacy hardware. Specifically, WPA requires only the TKIP cipher that is derived from the original WEP cipher. 802.11i permits use of TKIP but also requires support for a stronger cipher, AES-CCM, for encrypting data. The AES cipher was not required in WPA because it was deemed too computationally costly to be implemented on legacy hardware. The other standard to be aware of is 802.11e. It defines protocols for deploying multimedia applications, such as streaming video and voice over IP (VoIP), in an 802.11 network. Like 802.11i, 802.11e also has a precursor specification termed WME (later renamed WMM) that has been defined by an industry group as a subset of 802.11e that can be deployed now to enable multimedia applications while waiting for the final ratification of 802.11e. The most important thing to know about 802.11e and WME/WMM is that it enables prioritized traffic over a wireless network through Quality of Service (QoS) protocols and enhanced media access protocols. Proper implementation of these protocols enables high speed bursting of data and prioritized traffic flow. FreeBSD supports networks that operate using 802.11a, 802.11b, and 802.11g. The WPA and 802.11i security protocols are likewise supported (in conjunction with any of 11a, 11b, and 11g) and QoS and traffic prioritization required by the WME/WMM protocols are supported for a limited set of wireless devices. [[network-wireless-quick-start]] === Quick Start Connecting a computer to an existing wireless network is a very common situation. This procedure shows the steps required. [.procedure] . Obtain the SSID (Service Set Identifier) and PSK (Pre-Shared Key) for the wireless network from the network administrator. . Identify the wireless adapter. The FreeBSD [.filename]#GENERIC# kernel includes drivers for many common wireless adapters. If the wireless adapter is one of those models, it will be shown in the output from man:ifconfig[8]: + [source,shell] .... % ifconfig | grep -B3 -i wireless .... -+ ++ On FreeBSD 11 or higher, use this command instead: + [source,shell] .... % sysctl net.wlan.devices .... -+ ++ If a wireless adapter is not listed, an additional kernel module might be required, or it might be a model not supported by FreeBSD. -+ ++ This example shows the Atheros `ath0` wireless adapter. . Add an entry for this network to [.filename]#/etc/wpa_supplicant.conf#. If the file does not exist, create it. Replace _myssid_ and _mypsk_ with the SSID and PSK provided by the network administrator. + [.programlisting] .... network={ ssid="myssid" psk="mypsk" } .... . Add entries to [.filename]#/etc/rc.conf# to configure the network on startup: + [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="WPA SYNCDHCP" .... . Restart the computer, or restart the network service to connect to the network: + [source,shell] .... # service netif restart .... [[network-wireless-basic]] === Basic Setup ==== Kernel Configuration To use wireless networking, a wireless networking card is needed and the kernel needs to be configured with the appropriate wireless networking support. The kernel is separated into multiple modules so that only the required support needs to be configured. The most commonly used wireless devices are those that use parts made by Atheros. These devices are supported by man:ath[4] and require the following line to be added to [.filename]#/boot/loader.conf#: [.programlisting] .... if_ath_load="YES" .... The Atheros driver is split up into three separate pieces: the driver (man:ath[4]), the hardware support layer that handles chip-specific functions (man:ath_hal[4]), and an algorithm for selecting the rate for transmitting frames. When this support is loaded as kernel modules, any dependencies are automatically handled. To load support for a different type of wireless device, specify the module for that device. This example is for devices based on the Intersil Prism parts (man:wi[4]) driver: [.programlisting] .... if_wi_load="YES" .... [NOTE] ==== The examples in this section use an man:ath[4] device and the device name in the examples must be changed according to the configuration. A list of available wireless drivers and supported adapters can be found in the FreeBSD Hardware Notes, available on the https://www.FreeBSD.org/releases/[Release Information] page of the FreeBSD website. If a native FreeBSD driver for the wireless device does not exist, it may be possible to use the Windows(R) driver with the help of the crossref:config[config-network-ndis,NDIS] driver wrapper. ==== In addition, the modules that implement cryptographic support for the security protocols to use must be loaded. These are intended to be dynamically loaded on demand by the man:wlan[4] module, but for now they must be manually configured. The following modules are available: man:wlan_wep[4], man:wlan_ccmp[4], and man:wlan_tkip[4]. The man:wlan_ccmp[4] and man:wlan_tkip[4] drivers are only needed when using the WPA or 802.11i security protocols. If the network does not use encryption, man:wlan_wep[4] support is not needed. To load these modules at boot time, add the following lines to [.filename]#/boot/loader.conf#: [.programlisting] .... wlan_wep_load="YES" wlan_ccmp_load="YES" wlan_tkip_load="YES" .... Once this information has been added to [.filename]#/boot/loader.conf#, reboot the FreeBSD box. Alternately, load the modules by hand using man:kldload[8]. [NOTE] ==== For users who do not want to use modules, it is possible to compile these drivers into the kernel by adding the following lines to a custom kernel configuration file: [.programlisting] .... device wlan # 802.11 support device wlan_wep # 802.11 WEP support device wlan_ccmp # 802.11 CCMP support device wlan_tkip # 802.11 TKIP support device wlan_amrr # AMRR transmit rate control algorithm device ath # Atheros pci/cardbus NIC's device ath_hal # pci/cardbus chip support options AH_SUPPORT_AR5416 # enable AR5416 tx/rx descriptors device ath_rate_sample # SampleRate tx rate control for ath .... With this information in the kernel configuration file, recompile the kernel and reboot the FreeBSD machine. ==== Information about the wireless device should appear in the boot messages, like this: [source,shell] .... ath0: mem 0x88000000-0x8800ffff irq 11 at device 0.0 on cardbus1 ath0: [ITHREAD] ath0: AR2413 mac 7.9 RF2413 phy 4.5 .... ==== Setting the Correct Region Since the regulatory situation is different in various parts of the world, it is necessary to correctly set the domains that apply to your location to have the correct information about what channels can be used. The available region definitions can be found in [.filename]#/etc/regdomain.xml#. To set the data at runtime, use `ifconfig`: [source,shell] .... # ifconfig wlan0 regdomain ETSI country AT .... To persist the settings, add it to [.filename]#/etc/rc.conf#: [source,shell] .... # sysrc create_args_wlan0="country AT regdomain ETSI" .... === Infrastructure Mode Infrastructure (BSS) mode is the mode that is typically used. In this mode, a number of wireless access points are connected to a wired network. Each wireless network has its own name, called the SSID. Wireless clients connect to the wireless access points. ==== FreeBSD Clients ===== How to Find Access Points To scan for available networks, use man:ifconfig[8]. This request may take a few moments to complete as it requires the system to switch to each available wireless frequency and probe for available access points. Only the superuser can initiate a scan: [source,shell] .... # ifconfig wlan0 create wlandev ath0 # ifconfig wlan0 up scan SSID/MESH ID BSSID CHAN RATE S:N INT CAPS dlinkap 00:13:46:49:41:76 11 54M -90:96 100 EPS WPA WME freebsdap 00:11:95:c3:0d:ac 1 54M -83:96 100 EPS WPA .... [NOTE] ==== The interface must be `up` before it can scan. Subsequent scan requests do not require the interface to be marked as up again. ==== The output of a scan request lists each BSS/IBSS network found. Besides listing the name of the network, the `SSID`, the output also shows the `BSSID`, which is the MAC address of the access point. The `CAPS` field identifies the type of each network and the capabilities of the stations operating there: .Station Capability Codes [cols="1,1", frame="none", options="header"] |=== | Capability Code | Meaning |`E` |Extended Service Set (ESS). Indicates that the station is part of an infrastructure network rather than an IBSS/ad-hoc network. |`I` |IBSS/ad-hoc network. Indicates that the station is part of an ad-hoc network rather than an ESS network. |`P` |Privacy. Encryption is required for all data frames exchanged within the BSS using cryptographic means such as WEP, TKIP or AES-CCMP. |`S` |Short Preamble. Indicates that the network is using short preambles, defined in 802.11b High Rate/DSSS PHY, and utilizes a 56 bit sync field rather than the 128 bit field used in long preamble mode. |`s` |Short slot time. Indicates that the 802.11g network is using a short slot time because there are no legacy (802.11b) stations present. |=== One can also display the current list of known networks with: [source,shell] .... # ifconfig wlan0 list scan .... This information may be updated automatically by the adapter or manually with a `scan` request. Old data is automatically removed from the cache, so over time this list may shrink unless more scans are done. ===== Basic Settings This section provides a simple example of how to make the wireless network adapter work in FreeBSD without encryption. Once familiar with these concepts, it is strongly recommend to use <> to set up the wireless network. There are three basic steps to configure a wireless network: select an access point, authenticate the station, and configure an IP address. The following sections discuss each step. ====== Selecting an Access Point Most of the time, it is sufficient to let the system choose an access point using the builtin heuristics. This is the default behavior when an interface is marked as up or it is listed in [.filename]#/etc/rc.conf#: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="DHCP" .... If there are multiple access points, a specific one can be selected by its SSID: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="ssid your_ssid_here DHCP" .... In an environment where there are multiple access points with the same SSID, which is often done to simplify roaming, it may be necessary to associate to one specific device. In this case, the BSSID of the access point can be specified, with or without the SSID: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="ssid your_ssid_here bssid xx:xx:xx:xx:xx:xx DHCP" .... There are other ways to constrain the choice of an access point, such as limiting the set of frequencies the system will scan on. This may be useful for a multi-band wireless card as scanning all the possible channels can be time-consuming. To limit operation to a specific band, use the `mode` parameter: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="mode 11g ssid your_ssid_here DHCP" .... This example will force the card to operate in 802.11g, which is defined only for 2.4GHz frequencies so any 5GHz channels will not be considered. This can also be achieved with the `channel` parameter, which locks operation to one specific frequency, and the `chanlist` parameter, to specify a list of channels for scanning. More information about these parameters can be found in man:ifconfig[8]. ====== Authentication Once an access point is selected, the station needs to authenticate before it can pass data. Authentication can happen in several ways. The most common scheme, open authentication, allows any station to join the network and communicate. This is the authentication to use for test purposes the first time a wireless network is setup. Other schemes require cryptographic handshakes to be completed before data traffic can flow, either using pre-shared keys or secrets, or more complex schemes that involve backend services such as RADIUS. Open authentication is the default setting. The next most common setup is WPA-PSK, also known as WPA Personal, which is described in <>. [NOTE] ==== If using an Apple(R) AirPort(R) Extreme base station for an access point, shared-key authentication together with a WEP key needs to be configured. This can be configured in [.filename]#/etc/rc.conf# or by using man:wpa_supplicant[8]. For a single AirPort(R) base station, access can be configured with: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="authmode shared wepmode on weptxkey 1 wepkey 01234567 DHCP" .... In general, shared key authentication should be avoided because it uses the WEP key material in a highly-constrained manner, making it even easier to crack the key. If WEP must be used for compatibility with legacy devices, it is better to use WEP with `open` authentication. More information regarding WEP can be found in <>. ==== ====== Getting an IP Address with DHCP Once an access point is selected and the authentication parameters are set, an IP address must be obtained in order to communicate. Most of the time, the IP address is obtained via DHCP. To achieve that, edit [.filename]#/etc/rc.conf# and add `DHCP` to the configuration for the device: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="DHCP" .... The wireless interface is now ready to bring up: [source,shell] .... # service netif start .... Once the interface is running, use man:ifconfig[8] to see the status of the interface [.filename]#ath0#: [source,shell] .... # ifconfig wlan0 wlan0: flags=8843 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.1.100 netmask 0xffffff00 broadcast 192.168.1.255 media: IEEE 802.11 Wireless Ethernet OFDM/54Mbps mode 11g status: associated ssid dlinkap channel 11 (2462 Mhz 11g) bssid 00:13:46:49:41:76 country US ecm authmode OPEN privacy OFF txpower 21.5 bmiss 7 scanvalid 60 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst .... The `status: associated` line means that it is connected to the wireless network. The `bssid 00:13:46:49:41:76` is the MAC address of the access point and `authmode OPEN` indicates that the communication is not encrypted. ====== Static IP Address If an IP address cannot be obtained from a DHCP server, set a fixed IP address. Replace the `DHCP` keyword shown above with the address information. Be sure to retain any other parameters for selecting the access point: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="inet 192.168.1.100 netmask 255.255.255.0 ssid your_ssid_here" .... [[network-wireless-wpa]] ===== WPA Wi-Fi Protected Access (WPA) is a security protocol used together with 802.11 networks to address the lack of proper authentication and the weakness of WEP. WPA leverages the 802.1X authentication protocol and uses one of several ciphers instead of WEP for data integrity. The only cipher required by WPA is the Temporary Key Integrity Protocol (TKIP). TKIP is a cipher that extends the basic RC4 cipher used by WEP by adding integrity checking, tamper detection, and measures for responding to detected intrusions. TKIP is designed to work on legacy hardware with only software modification. It represents a compromise that improves security but is still not entirely immune to attack. WPA also specifies the AES-CCMP cipher as an alternative to TKIP, and that is preferred when possible. For this specification, the term WPA2 or RSN is commonly used. WPA defines authentication and encryption protocols. Authentication is most commonly done using one of two techniques: by 802.1X and a backend authentication service such as RADIUS, or by a minimal handshake between the station and the access point using a pre-shared secret. The former is commonly termed WPA Enterprise and the latter is known as WPA Personal. Since most people will not set up a RADIUS backend server for their wireless network, WPA-PSK is by far the most commonly encountered configuration for WPA. The control of the wireless connection and the key negotiation or authentication with a server is done using man:wpa_supplicant[8]. This program requires a configuration file, [.filename]#/etc/wpa_supplicant.conf#, to run. More information regarding this file can be found in man:wpa_supplicant.conf[5]. [[network-wireless-wpa-wpa-psk]] ====== WPA-PSK WPA-PSK, also known as WPA Personal, is based on a pre-shared key (PSK) which is generated from a given password and used as the master key in the wireless network. This means every wireless user will share the same key. WPA-PSK is intended for small networks where the use of an authentication server is not possible or desired. [WARNING] ==== Always use strong passwords that are sufficiently long and made from a rich alphabet so that they will not be easily guessed or attacked. ==== The first step is the configuration of [.filename]#/etc/wpa_supplicant.conf# with the SSID and the pre-shared key of the network: [.programlisting] .... network={ ssid="freebsdap" psk="freebsdmall" } .... Then, in [.filename]#/etc/rc.conf#, indicate that the wireless device configuration will be done with WPA and the IP address will be obtained with DHCP: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="WPA DHCP" .... Then, bring up the interface: [source,shell] .... # service netif start Starting wpa_supplicant. DHCPDISCOVER on wlan0 to 255.255.255.255 port 67 interval 5 DHCPDISCOVER on wlan0 to 255.255.255.255 port 67 interval 6 DHCPOFFER from 192.168.0.1 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 DHCPACK from 192.168.0.1 bound to 192.168.0.254 -- renewal in 300 seconds. wlan0: flags=8843 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.254 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet OFDM/36Mbps mode 11g status: associated ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 3:128-bit txpower 21.5 bmiss 7 scanvalid 450 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst roaming MANUAL .... Or, try to configure the interface manually using the information in [.filename]#/etc/wpa_supplicant.conf#: [source,shell] .... # wpa_supplicant -i wlan0 -c /etc/wpa_supplicant.conf Trying to associate with 00:11:95:c3:0d:ac (SSID='freebsdap' freq=2412 MHz) Associated with 00:11:95:c3:0d:ac WPA: Key negotiation completed with 00:11:95:c3:0d:ac [PTK=CCMP GTK=CCMP] CTRL-EVENT-CONNECTED - Connection to 00:11:95:c3:0d:ac completed (auth) [id=0 id_str=] .... The next operation is to launch man:dhclient[8] to get the IP address from the DHCP server: [source,shell] .... # dhclient wlan0 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 DHCPACK from 192.168.0.1 bound to 192.168.0.254 -- renewal in 300 seconds. # ifconfig wlan0 wlan0: flags=8843 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.254 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet OFDM/36Mbps mode 11g status: associated ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 3:128-bit txpower 21.5 bmiss 7 scanvalid 450 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst roaming MANUAL .... [NOTE] ==== If [.filename]#/etc/rc.conf# has an `ifconfig_wlan0="DHCP"` entry, man:dhclient[8] will be launched automatically after man:wpa_supplicant[8] associates with the access point. ==== If DHCP is not possible or desired, set a static IP address after man:wpa_supplicant[8] has authenticated the station: [source,shell] .... # ifconfig wlan0 inet 192.168.0.100 netmask 255.255.255.0 # ifconfig wlan0 wlan0: flags=8843 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.100 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet OFDM/36Mbps mode 11g status: associated ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 3:128-bit txpower 21.5 bmiss 7 scanvalid 450 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst roaming MANUAL .... When DHCP is not used, the default gateway and the nameserver also have to be manually set: [source,shell] .... # route add default your_default_router # echo "nameserver your_DNS_server" >> /etc/resolv.conf .... [[network-wireless-wpa-eap-tls]] ====== WPA with EAP-TLS The second way to use WPA is with an 802.1X backend authentication server. In this case, WPA is called WPA Enterprise to differentiate it from the less secure WPA Personal. Authentication in WPA Enterprise is based on the Extensible Authentication Protocol (EAP). EAP does not come with an encryption method. Instead, EAP is embedded inside an encrypted tunnel. There are many EAP authentication methods, but EAP-TLS, EAP-TTLS, and EAP-PEAP are the most common. EAP with Transport Layer Security (EAP-TLS) is a well-supported wireless authentication protocol since it was the first EAP method to be certified by the http://www.wi-fi.org/[Wi-Fi Alliance]. EAP-TLS requires three certificates to run: the certificate of the Certificate Authority (CA) installed on all machines, the server certificate for the authentication server, and one client certificate for each wireless client. In this EAP method, both the authentication server and wireless client authenticate each other by presenting their respective certificates, and then verify that these certificates were signed by the organization's CA. As previously, the configuration is done via [.filename]#/etc/wpa_supplicant.conf#: [.programlisting] .... network={ ssid="freebsdap" <.> proto=RSN <.> key_mgmt=WPA-EAP <.> eap=TLS <.> identity="loader" <.> ca_cert="/etc/certs/cacert.pem" <.> client_cert="/etc/certs/clientcert.pem" <.> private_key="/etc/certs/clientkey.pem" <.> private_key_passwd="freebsdmallclient" <.> } .... <.> This field indicates the network name (SSID). <.> This example uses the RSN IEEE(R) 802.11i protocol, also known as WPA2. <.> The `key_mgmt` line refers to the key management protocol to use. In this example, it is WPA using EAP authentication. <.> This field indicates the EAP method for the connection. <.> The `identity` field contains the identity string for EAP. <.> The `ca_cert` field indicates the pathname of the CA certificate file. This file is needed to verify the server certificate. <.> The `client_cert` line gives the pathname to the client certificate file. This certificate is unique to each wireless client of the network. <.> The `private_key` field is the pathname to the client certificate private key file. <.> The `private_key_passwd` field contains the passphrase for the private key. Then, add the following lines to [.filename]#/etc/rc.conf#: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="WPA DHCP" .... The next step is to bring up the interface: [source,shell] .... # service netif start Starting wpa_supplicant. DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 7 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 15 DHCPACK from 192.168.0.20 bound to 192.168.0.254 -- renewal in 300 seconds. wlan0: flags=8843 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.254 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet DS/11Mbps mode 11g status: associated ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 3:128-bit txpower 21.5 bmiss 7 scanvalid 450 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst roaming MANUAL .... It is also possible to bring up the interface manually using man:wpa_supplicant[8] and man:ifconfig[8]. [[network-wireless-wpa-eap-ttls]] ====== WPA with EAP-TTLS With EAP-TLS, both the authentication server and the client need a certificate. With EAP-TTLS, a client certificate is optional. This method is similar to a web server which creates a secure SSL tunnel even if visitors do not have client-side certificates. EAP-TTLS uses an encrypted TLS tunnel for safe transport of the authentication data. The required configuration can be added to [.filename]#/etc/wpa_supplicant.conf#: [.programlisting] .... network={ ssid="freebsdap" proto=RSN key_mgmt=WPA-EAP eap=TTLS <.> identity="test" <.> password="test" <.> ca_cert="/etc/certs/cacert.pem" <.> phase2="auth=MD5" <.> } .... <.> This field specifies the EAP method for the connection. <.> The `identity` field contains the identity string for EAP authentication inside the encrypted TLS tunnel. <.> The `password` field contains the passphrase for the EAP authentication. <.> The `ca_cert` field indicates the pathname of the CA certificate file. This file is needed to verify the server certificate. <.> This field specifies the authentication method used in the encrypted TLS tunnel. In this example, EAP with MD5-Challenge is used. The "inner authentication" phase is often called "phase2". Next, add the following lines to [.filename]#/etc/rc.conf#: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="WPA DHCP" .... The next step is to bring up the interface: [source,shell] .... # service netif start Starting wpa_supplicant. DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 7 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 15 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 21 DHCPACK from 192.168.0.20 bound to 192.168.0.254 -- renewal in 300 seconds. wlan0: flags=8843 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.254 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet DS/11Mbps mode 11g status: associated ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 3:128-bit txpower 21.5 bmiss 7 scanvalid 450 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst roaming MANUAL .... [[network-wireless-wpa-eap-peap]] ====== WPA with EAP-PEAP [NOTE] ==== PEAPv0/EAP-MSCHAPv2 is the most common PEAP method. In this chapter, the term PEAP is used to refer to that method. ==== Protected EAP (PEAP) is designed as an alternative to EAP-TTLS and is the most used EAP standard after EAP-TLS. In a network with mixed operating systems, PEAP should be the most supported standard after EAP-TLS. PEAP is similar to EAP-TTLS as it uses a server-side certificate to authenticate clients by creating an encrypted TLS tunnel between the client and the authentication server, which protects the ensuing exchange of authentication information. PEAP authentication differs from EAP-TTLS as it broadcasts the username in the clear and only the password is sent in the encrypted TLS tunnel. EAP-TTLS will use the TLS tunnel for both the username and password. Add the following lines to [.filename]#/etc/wpa_supplicant.conf# to configure the EAP-PEAP related settings: [.programlisting] .... network={ ssid="freebsdap" proto=RSN key_mgmt=WPA-EAP eap=PEAP <.> identity="test" <.> password="test" <.> ca_cert="/etc/certs/cacert.pem" <.> phase1="peaplabel=0" <.> phase2="auth=MSCHAPV2" <.> } .... <.> This field specifies the EAP method for the connection. <.> The `identity` field contains the identity string for EAP authentication inside the encrypted TLS tunnel. <.> The `password` field contains the passphrase for the EAP authentication. <.> The `ca_cert` field indicates the pathname of the CA certificate file. This file is needed to verify the server certificate. <.> This field contains the parameters for the first phase of authentication, the TLS tunnel. According to the authentication server used, specify a specific label for authentication. Most of the time, the label will be "client EAP encryption" which is set by using `peaplabel=0`. More information can be found in man:wpa_supplicant.conf[5]. <.> This field specifies the authentication protocol used in the encrypted TLS tunnel. In the case of PEAP, it is `auth=MSCHAPV2`. Add the following to [.filename]#/etc/rc.conf#: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="WPA DHCP" .... Then, bring up the interface: [source,shell] .... # service netif start Starting wpa_supplicant. DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 7 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 15 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 21 DHCPACK from 192.168.0.20 bound to 192.168.0.254 -- renewal in 300 seconds. wlan0: flags=8843 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.254 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet DS/11Mbps mode 11g status: associated ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 3:128-bit txpower 21.5 bmiss 7 scanvalid 450 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst roaming MANUAL .... [[network-wireless-wep]] ===== WEP Wired Equivalent Privacy (WEP) is part of the original 802.11 standard. There is no authentication mechanism, only a weak form of access control which is easily cracked. WEP can be set up using man:ifconfig[8]: [source,shell] .... # ifconfig wlan0 create wlandev ath0 # ifconfig wlan0 inet 192.168.1.100 netmask 255.255.255.0 \ ssid my_net wepmode on weptxkey 3 wepkey 3:0x3456789012 .... * The `weptxkey` specifies which WEP key will be used in the transmission. This example uses the third key. This must match the setting on the access point. When unsure which key is used by the access point, try `1` (the first key) for this value. * The `wepkey` selects one of the WEP keys. It should be in the format _index:key_. Key `1` is used by default; the index only needs to be set when using a key other than the first key. + [NOTE] ==== Replace the `0x3456789012` with the key configured for use on the access point. ==== Refer to man:ifconfig[8] for further information. The man:wpa_supplicant[8] facility can be used to configure a wireless interface with WEP. The example above can be set up by adding the following lines to [.filename]#/etc/wpa_supplicant.conf#: [.programlisting] .... network={ ssid="my_net" key_mgmt=NONE wep_key3=3456789012 wep_tx_keyidx=3 } .... Then: [source,shell] .... # wpa_supplicant -i wlan0 -c /etc/wpa_supplicant.conf Trying to associate with 00:13:46:49:41:76 (SSID='dlinkap' freq=2437 MHz) Associated with 00:13:46:49:41:76 .... === Ad-hoc Mode IBSS mode, also called ad-hoc mode, is designed for point to point connections. For example, to establish an ad-hoc network between the machines `A` and `B`, choose two IP addresses and a SSID. On `A`: [source,shell] .... # ifconfig wlan0 create wlandev ath0 wlanmode adhoc # ifconfig wlan0 inet 192.168.0.1 netmask 255.255.255.0 ssid freebsdap # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether 00:11:95:c3:0d:ac inet 192.168.0.1 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet autoselect mode 11g status: running ssid freebsdap channel 2 (2417 Mhz 11g) bssid 02:11:95:c3:0d:ac country US ecm authmode OPEN privacy OFF txpower 21.5 scanvalid 60 protmode CTS wme burst .... The `adhoc` parameter indicates that the interface is running in IBSS mode. `B` should now be able to detect `A`: [source,shell] .... # ifconfig wlan0 create wlandev ath0 wlanmode adhoc # ifconfig wlan0 up scan SSID/MESH ID BSSID CHAN RATE S:N INT CAPS freebsdap 02:11:95:c3:0d:ac 2 54M -64:-96 100 IS WME .... The `I` in the output confirms that `A` is in ad-hoc mode. Now, configure `B` with a different IP address: [source,shell] .... # ifconfig wlan0 inet 192.168.0.2 netmask 255.255.255.0 ssid freebsdap # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.2 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet autoselect mode 11g status: running ssid freebsdap channel 2 (2417 Mhz 11g) bssid 02:11:95:c3:0d:ac country US ecm authmode OPEN privacy OFF txpower 21.5 scanvalid 60 protmode CTS wme burst .... Both `A` and `B` are now ready to exchange information. [[network-wireless-ap]] === FreeBSD Host Access Points FreeBSD can act as an Access Point (AP) which eliminates the need to buy a hardware AP or run an ad-hoc network. This can be particularly useful when a FreeBSD machine is acting as a gateway to another network such as the Internet. [[network-wireless-ap-basic]] ==== Basic Settings Before configuring a FreeBSD machine as an AP, the kernel must be configured with the appropriate networking support for the wireless card as well as the security protocols being used. For more details, see <>. [NOTE] ==== The NDIS driver wrapper for Windows(R) drivers does not currently support AP operation. Only native FreeBSD wireless drivers support AP mode. ==== Once wireless networking support is loaded, check if the wireless device supports the host-based access point mode, also known as hostap mode: [source,shell] .... # ifconfig wlan0 create wlandev ath0 # ifconfig wlan0 list caps drivercaps=6f85edc1 cryptocaps=1f .... This output displays the card's capabilities. The `HOSTAP` word confirms that this wireless card can act as an AP. Various supported ciphers are also listed: WEP, TKIP, and AES. This information indicates which security protocols can be used on the AP. The wireless device can only be put into hostap mode during the creation of the network pseudo-device, so a previously created device must be destroyed first: [source,shell] .... # ifconfig wlan0 destroy .... then regenerated with the correct option before setting the other parameters: [source,shell] .... # ifconfig wlan0 create wlandev ath0 wlanmode hostap # ifconfig wlan0 inet 192.168.0.1 netmask 255.255.255.0 ssid freebsdap mode 11g channel 1 .... Use man:ifconfig[8] again to see the status of the [.filename]#wlan0# interface: [source,shell] .... # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether 00:11:95:c3:0d:ac inet 192.168.0.1 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet autoselect mode 11g status: running ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode OPEN privacy OFF txpower 21.5 scanvalid 60 protmode CTS wme burst dtimperiod 1 -dfs .... The `hostap` parameter indicates the interface is running in the host-based access point mode. The interface configuration can be done automatically at boot time by adding the following lines to [.filename]#/etc/rc.conf#: [.programlisting] .... wlans_ath0="wlan0" create_args_wlan0="wlanmode hostap" ifconfig_wlan0="inet 192.168.0.1 netmask 255.255.255.0 ssid freebsdap mode 11g channel 1" .... ==== Host-based Access Point Without Authentication or Encryption Although it is not recommended to run an AP without any authentication or encryption, this is a simple way to check if the AP is working. This configuration is also important for debugging client issues. Once the AP is configured, initiate a scan from another wireless machine to find the AP: [source,shell] .... # ifconfig wlan0 create wlandev ath0 # ifconfig wlan0 up scan SSID/MESH ID BSSID CHAN RATE S:N INT CAPS freebsdap 00:11:95:c3:0d:ac 1 54M -66:-96 100 ES WME .... The client machine found the AP and can be associated with it: [source,shell] .... # ifconfig wlan0 inet 192.168.0.2 netmask 255.255.255.0 ssid freebsdap # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.2 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet OFDM/54Mbps mode 11g status: associated ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode OPEN privacy OFF txpower 21.5 bmiss 7 scanvalid 60 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst .... [[network-wireless-ap-wpa]] ==== WPA2 Host-based Access Point This section focuses on setting up a FreeBSD access point using the WPA2 security protocol. More details regarding WPA and the configuration of WPA-based wireless clients can be found in <>. The man:hostapd[8] daemon is used to deal with client authentication and key management on the WPA2-enabled AP. The following configuration operations are performed on the FreeBSD machine acting as the AP. Once the AP is correctly working, man:hostapd[8] can be automatically started at boot with this line in [.filename]#/etc/rc.conf#: [.programlisting] .... hostapd_enable="YES" .... Before trying to configure man:hostapd[8], first configure the basic settings introduced in <>. ===== WPA2-PSK WPA2-PSK is intended for small networks where the use of a backend authentication server is not possible or desired. The configuration is done in [.filename]#/etc/hostapd.conf#: [.programlisting] .... interface=wlan0 <.> debug=1 <.> ctrl_interface=/var/run/hostapd <.> ctrl_interface_group=wheel <.> ssid=freebsdap <.> wpa=2 <.> wpa_passphrase=freebsdmall <.> wpa_key_mgmt=WPA-PSK <.> wpa_pairwise=CCMP <.> .... <.> Wireless interface used for the access point. <.> Level of verbosity used during the execution of man:hostapd[8]. A value of `1` represents the minimal level. <.> Pathname of the directory used by man:hostapd[8] to store domain socket files for communication with external programs such as man:hostapd_cli[8]. The default value is used in this example. <.> The group allowed to access the control interface files. <.> The wireless network name, or SSID, that will appear in wireless scans. <.> Enable WPA and specify which WPA authentication protocol will be required. A value of `2` configures the AP for WPA2 and is recommended. Set to `1` only if the obsolete WPA is required. <.> ASCII passphrase for WPA authentication. <.> The key management protocol to use. This example sets WPA-PSK. <.> Encryption algorithms accepted by the access point. In this example, only the CCMP (AES) cipher is accepted. CCMP is an alternative to TKIP and is strongly preferred when possible. TKIP should be allowed only when there are stations incapable of using CCMP. The next step is to start man:hostapd[8]: [source,shell] .... # service hostapd forcestart .... [source,shell] .... # ifconfig wlan0 wlan0: flags=8943 metric 0 mtu 1500 ether 04:f0:21:16:8e:10 inet6 fe80::6f0:21ff:fe16:8e10%wlan0 prefixlen 64 scopeid 0x9 nd6 options=21 media: IEEE 802.11 Wireless Ethernet autoselect mode 11na status: running ssid No5ignal channel 36 (5180 MHz 11a ht/40+) bssid 04:f0:21:16:8e:10 country US ecm authmode WPA2/802.11i privacy MIXED deftxkey 2 AES-CCM 2:128-bit AES-CCM 3:128-bit txpower 17 mcastrate 6 mgmtrate 6 scanvalid 60 ampdulimit 64k ampdudensity 8 shortgi wme burst dtimperiod 1 -dfs groups: wlan .... Once the AP is running, the clients can associate with it. See <> for more details. It is possible to see the stations associated with the AP using `ifconfig _wlan0_ list sta`. ==== WEP Host-based Access Point It is not recommended to use WEP for setting up an AP since there is no authentication mechanism and the encryption is easily cracked. Some legacy wireless cards only support WEP and these cards will only support an AP without authentication or encryption. The wireless device can now be put into hostap mode and configured with the correct SSID and IP address: [source,shell] .... # ifconfig wlan0 create wlandev ath0 wlanmode hostap # ifconfig wlan0 inet 192.168.0.1 netmask 255.255.255.0 \ ssid freebsdap wepmode on weptxkey 3 wepkey 3:0x3456789012 mode 11g .... * The `weptxkey` indicates which WEP key will be used in the transmission. This example uses the third key as key numbering starts with `1`. This parameter must be specified in order to encrypt the data. * The `wepkey` sets the selected WEP key. It should be in the format _index:key_. If the index is not given, key `1` is set. The index needs to be set when using keys other than the first key. Use man:ifconfig[8] to see the status of the [.filename]#wlan0# interface: [source,shell] .... # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether 00:11:95:c3:0d:ac inet 192.168.0.1 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet autoselect mode 11g status: running ssid freebsdap channel 4 (2427 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode OPEN privacy ON deftxkey 3 wepkey 3:40-bit txpower 21.5 scanvalid 60 protmode CTS wme burst dtimperiod 1 -dfs .... From another wireless machine, it is now possible to initiate a scan to find the AP: [source,shell] .... # ifconfig wlan0 create wlandev ath0 # ifconfig wlan0 up scan SSID BSSID CHAN RATE S:N INT CAPS freebsdap 00:11:95:c3:0d:ac 1 54M 22:1 100 EPS .... In this example, the client machine found the AP and can associate with it using the correct parameters. See <> for more details. === Using Both Wired and Wireless Connections A wired connection provides better performance and reliability, while a wireless connection provides flexibility and mobility. Laptop users typically want to roam seamlessly between the two types of connections. On FreeBSD, it is possible to combine two or even more network interfaces together in a "failover" fashion. This type of configuration uses the most preferred and available connection from a group of network interfaces, and the operating system switches automatically when the link state changes. Link aggregation and failover is covered in <> and an example for using both wired and wireless connections is provided at <>. === Troubleshooting This section describes a number of steps to help troubleshoot common wireless networking problems. * If the access point is not listed when scanning, check that the configuration has not limited the wireless device to a limited set of channels. * If the device cannot associate with an access point, verify that the configuration matches the settings on the access point. This includes the authentication scheme and any security protocols. Simplify the configuration as much as possible. If using a security protocol such as WPA or WEP, configure the access point for open authentication and no security to see if traffic will pass. -+ ++ Debugging support is provided by man:wpa_supplicant[8]. Try running this utility manually with `-dd` and look at the system logs. * Once the system can associate with the access point, diagnose the network configuration using tools like man:ping[8]. * There are many lower-level debugging tools. Debugging messages can be enabled in the 802.11 protocol support layer using man:wlandebug[8]. For example, to enable console messages related to scanning for access points and the 802.11 protocol handshakes required to arrange communication: + [source,shell] .... # wlandebug -i wlan0 +scan+auth+debug+assoc net.wlan.0.debug: 0 => 0xc80000 .... -+ ++ Many useful statistics are maintained by the 802.11 layer and `wlanstats`, found in [.filename]#/usr/src/tools/tools/net80211#, will dump this information. These statistics should display all errors identified by the 802.11 layer. However, some errors are identified in the device drivers that lie below the 802.11 layer so they may not show up. To diagnose device-specific problems, refer to the drivers' documentation. If the above information does not help to clarify the problem, submit a problem report and include output from the above tools. [[network-usb-tethering]] == USB Tethering Many cellphones provide the option to share their data connection over USB (often called "tethering"). This feature uses one of RNDIS, CDC, or a custom Apple(R) iPhone(R)/iPad(R) protocol. * Android(TM) devices generally use the man:urndis[4] driver. * Apple(R) devices use the man:ipheth[4] driver. * Older devices will often use the man:cdce[4] driver. Before attaching a device, load the appropriate driver into the kernel: [source,shell] .... # kldload if_urndis # kldload if_cdce # kldload if_ipheth .... Once the device is attached ``ue``_0_ will be available for use like a normal network device. Be sure that the "USB tethering" option is enabled on the device. To make this change permanent and load the driver as a module at boot time, place the appropriate line of the following in [.filename]#/boot/loader.conf#: [source,shell] .... if_urndis_load="YES" if_cdce_load="YES" if_ipheth_load="YES" .... [[network-bluetooth]] == Bluetooth Bluetooth is a wireless technology for creating personal networks operating in the 2.4 GHz unlicensed band, with a range of 10 meters. Networks are usually formed ad-hoc from portable devices such as cellular phones, handhelds, and laptops. Unlike Wi-Fi wireless technology, Bluetooth offers higher level service profiles, such as FTP-like file servers, file pushing, voice transport, serial line emulation, and more. This section describes the use of a USB Bluetooth dongle on a FreeBSD system. It then describes the various Bluetooth protocols and utilities. === Loading Bluetooth Support The Bluetooth stack in FreeBSD is implemented using the man:netgraph[4] framework. A broad variety of Bluetooth USB dongles is supported by man:ng_ubt[4]. Broadcom BCM2033 based Bluetooth devices are supported by the man:ubtbcmfw[4] and man:ng_ubt[4] drivers. The 3Com Bluetooth PC Card 3CRWB60-A is supported by the man:ng_bt3c[4] driver. Serial and UART based Bluetooth devices are supported by man:sio[4], man:ng_h4[4], and man:hcseriald[8]. Before attaching a device, determine which of the above drivers it uses, then load the driver. For example, if the device uses the man:ng_ubt[4] driver: [source,shell] .... # kldload ng_ubt .... If the Bluetooth device will be attached to the system during system startup, the system can be configured to load the module at boot time by adding the driver to [.filename]#/boot/loader.conf#: [.programlisting] .... ng_ubt_load="YES" .... Once the driver is loaded, plug in the USB dongle. If the driver load was successful, output similar to the following should appear on the console and in [.filename]#/var/log/messages#: [source,shell] .... ubt0: vendor 0x0a12 product 0x0001, rev 1.10/5.25, addr 2 ubt0: Interface 0 endpoints: interrupt=0x81, bulk-in=0x82, bulk-out=0x2 ubt0: Interface 1 (alt.config 5) endpoints: isoc-in=0x83, isoc-out=0x3, wMaxPacketSize=49, nframes=6, buffer size=294 .... To start and stop the Bluetooth stack, use its startup script. It is a good idea to stop the stack before unplugging the device. Starting the bluetooth stack might require man:hcsecd[8] to be started. When starting the stack, the output should be similar to the following: [source,shell] .... # service bluetooth start ubt0 BD_ADDR: 00:02:72:00:d4:1a Features: 0xff 0xff 0xf 00 00 00 00 00 <3-Slot> <5-Slot> Max. ACL packet size: 192 bytes Number of ACL packets: 8 Max. SCO packet size: 64 bytes Number of SCO packets: 8 .... === Finding Other Bluetooth Devices The Host Controller Interface (HCI) provides a uniform method for accessing Bluetooth baseband capabilities. In FreeBSD, a netgraph HCI node is created for each Bluetooth device. For more details, refer to man:ng_hci[4]. One of the most common tasks is discovery of Bluetooth devices within RF proximity. This operation is called _inquiry_. Inquiry and other HCI related operations are done using man:hccontrol[8]. The example below shows how to find out which Bluetooth devices are in range. The list of devices should be displayed in a few seconds. Note that a remote device will only answer the inquiry if it is set to _discoverable_ mode. [source,shell] .... % hccontrol -n ubt0hci inquiry Inquiry result, num_responses=1 Inquiry result #0 BD_ADDR: 00:80:37:29:19:a4 Page Scan Rep. Mode: 0x1 Page Scan Period Mode: 00 Page Scan Mode: 00 Class: 52:02:04 Clock offset: 0x78ef Inquiry complete. Status: No error [00] .... The `BD_ADDR` is the unique address of a Bluetooth device, similar to the MAC address of a network card. This address is needed for further communication with a device and it is possible to assign a human readable name to a `BD_ADDR`. Information regarding the known Bluetooth hosts is contained in [.filename]#/etc/bluetooth/hosts#. The following example shows how to obtain the human readable name that was assigned to the remote device: [source,shell] .... % hccontrol -n ubt0hci remote_name_request 00:80:37:29:19:a4 BD_ADDR: 00:80:37:29:19:a4 Name: Pav's T39 .... If an inquiry is performed on a remote Bluetooth device, it will find the computer as "your.host.name (ubt0)". The name assigned to the local device can be changed at any time. Remote devices can be assigned aliases in [.filename]#/etc/bluetooth/hosts#. More information about [.filename]#/etc/bluetooth/hosts# file might be found in man:bluetooth.hosts[5]. The Bluetooth system provides a point-to-point connection between two Bluetooth units, or a point-to-multipoint connection which is shared among several Bluetooth devices. The following example shows how to create a connection to a remote device: [source,shell] .... % hccontrol -n ubt0hci create_connection BT_ADDR .... `create_connection` accepts `BT_ADDR` as well as host aliases in [.filename]#/etc/bluetooth/hosts#. The following example shows how to obtain the list of active baseband connections for the local device: [source,shell] .... % hccontrol -n ubt0hci read_connection_list Remote BD_ADDR Handle Type Mode Role Encrypt Pending Queue State 00:80:37:29:19:a4 41 ACL 0 MAST NONE 0 0 OPEN .... A _connection handle_ is useful when termination of the baseband connection is required, though it is normally not required to do this by hand. The stack will automatically terminate inactive baseband connections. [source,shell] .... # hccontrol -n ubt0hci disconnect 41 Connection handle: 41 Reason: Connection terminated by local host [0x16] .... Type `hccontrol help` for a complete listing of available HCI commands. Most of the HCI commands do not require superuser privileges. === Device Pairing By default, Bluetooth communication is not authenticated, and any device can talk to any other device. A Bluetooth device, such as a cellular phone, may choose to require authentication to provide a particular service. Bluetooth authentication is normally done with a _PIN code_, an ASCII string up to 16 characters in length. The user is required to enter the same PIN code on both devices. Once the user has entered the PIN code, both devices will generate a _link key_. After that, the link key can be stored either in the devices or in a persistent storage. Next time, both devices will use the previously generated link key. This procedure is called _pairing_. Note that if the link key is lost by either device, the pairing must be repeated. The man:hcsecd[8] daemon is responsible for handling Bluetooth authentication requests. The default configuration file is [.filename]#/etc/bluetooth/hcsecd.conf#. An example section for a cellular phone with the PIN code set to `1234` is shown below: [.programlisting] .... device { bdaddr 00:80:37:29:19:a4; name "Pav's T39"; key nokey; pin "1234"; } .... The only limitation on PIN codes is length. Some devices, such as Bluetooth headsets, may have a fixed PIN code built in. The `-d` switch forces man:hcsecd[8] to stay in the foreground, so it is easy to see what is happening. Set the remote device to receive pairing and initiate the Bluetooth connection to the remote device. The remote device should indicate that pairing was accepted and request the PIN code. Enter the same PIN code listed in [.filename]#hcsecd.conf#. Now the computer and the remote device are paired. Alternatively, pairing can be initiated on the remote device. The following line can be added to [.filename]#/etc/rc.conf# to configure man:hcsecd[8] to start automatically on system start: [.programlisting] .... hcsecd_enable="YES" .... The following is a sample of the man:hcsecd[8] daemon output: [.programlisting] .... hcsecd[16484]: Got Link_Key_Request event from 'ubt0hci', remote bdaddr 0:80:37:29:19:a4 hcsecd[16484]: Found matching entry, remote bdaddr 0:80:37:29:19:a4, name 'Pav's T39', link key doesn't exist hcsecd[16484]: Sending Link_Key_Negative_Reply to 'ubt0hci' for remote bdaddr 0:80:37:29:19:a4 hcsecd[16484]: Got PIN_Code_Request event from 'ubt0hci', remote bdaddr 0:80:37:29:19:a4 hcsecd[16484]: Found matching entry, remote bdaddr 0:80:37:29:19:a4, name 'Pav's T39', PIN code exists hcsecd[16484]: Sending PIN_Code_Reply to 'ubt0hci' for remote bdaddr 0:80:37:29:19:a4 .... === Network Access with PPP Profiles A Dial-Up Networking (DUN) profile can be used to configure a cellular phone as a wireless modem for connecting to a dial-up Internet access server. It can also be used to configure a computer to receive data calls from a cellular phone. Network access with a PPP profile can be used to provide LAN access for a single Bluetooth device or multiple Bluetooth devices. It can also provide PC to PC connection using PPP networking over serial cable emulation. In FreeBSD, these profiles are implemented with man:ppp[8] and the man:rfcomm_pppd[8] wrapper which converts a Bluetooth connection into something PPP can use. Before a profile can be used, a new PPP label must be created in [.filename]#/etc/ppp/ppp.conf#. Consult man:rfcomm_pppd[8] for examples. In this example, man:rfcomm_pppd[8] is used to open a connection to a remote device with a `BD_ADDR` of `00:80:37:29:19:a4` on a DUNRFCOMM channel: [source,shell] .... # rfcomm_pppd -a 00:80:37:29:19:a4 -c -C dun -l rfcomm-dialup .... The actual channel number will be obtained from the remote device using the SDP protocol. It is possible to specify the RFCOMM channel by hand, and in this case man:rfcomm_pppd[8] will not perform the SDP query. Use man:sdpcontrol[8] to find out the RFCOMM channel on the remote device. In order to provide network access with the PPPLAN service, man:sdpd[8] must be running and a new entry for LAN clients must be created in [.filename]#/etc/ppp/ppp.conf#. Consult man:rfcomm_pppd[8] for examples. Finally, start the RFCOMMPPP server on a valid RFCOMM channel number. The RFCOMMPPP server will automatically register the Bluetooth LAN service with the local SDP daemon. The example below shows how to start the RFCOMMPPP server. [source,shell] .... # rfcomm_pppd -s -C 7 -l rfcomm-server .... === Bluetooth Protocols This section provides an overview of the various Bluetooth protocols, their function, and associated utilities. ==== Logical Link Control and Adaptation Protocol (L2CAP) The Logical Link Control and Adaptation Protocol (L2CAP) provides connection-oriented and connectionless data services to upper layer protocols. L2CAP permits higher level protocols and applications to transmit and receive L2CAP data packets up to 64 kilobytes in length. L2CAP is based around the concept of _channels_. A channel is a logical connection on top of a baseband connection, where each channel is bound to a single protocol in a many-to-one fashion. Multiple channels can be bound to the same protocol, but a channel cannot be bound to multiple protocols. Each L2CAP packet received on a channel is directed to the appropriate higher level protocol. Multiple channels can share the same baseband connection. In FreeBSD, a netgraph L2CAP node is created for each Bluetooth device. This node is normally connected to the downstream Bluetooth HCI node and upstream Bluetooth socket nodes. The default name for the L2CAP node is "devicel2cap". For more details refer to man:ng_l2cap[4]. A useful command is man:l2ping[8], which can be used to ping other devices. Some Bluetooth implementations might not return all of the data sent to them, so `0 bytes` in the following example is normal. [source,shell] .... # l2ping -a 00:80:37:29:19:a4 0 bytes from 0:80:37:29:19:a4 seq_no=0 time=48.633 ms result=0 0 bytes from 0:80:37:29:19:a4 seq_no=1 time=37.551 ms result=0 0 bytes from 0:80:37:29:19:a4 seq_no=2 time=28.324 ms result=0 0 bytes from 0:80:37:29:19:a4 seq_no=3 time=46.150 ms result=0 .... The man:l2control[8] utility is used to perform various operations on L2CAP nodes. This example shows how to obtain the list of logical connections (channels) and the list of baseband connections for the local device: [source,shell] .... % l2control -a 00:02:72:00:d4:1a read_channel_list L2CAP channels: Remote BD_ADDR SCID/ DCID PSM IMTU/ OMTU State 00:07:e0:00:0b:ca 66/ 64 3 132/ 672 OPEN % l2control -a 00:02:72:00:d4:1a read_connection_list L2CAP connections: Remote BD_ADDR Handle Flags Pending State 00:07:e0:00:0b:ca 41 O 0 OPEN .... Another diagnostic tool is man:btsockstat[1]. It is similar to man:netstat[1], but for Bluetooth network-related data structures. The example below shows the same logical connection as man:l2control[8] above. [source,shell] .... % btsockstat Active L2CAP sockets PCB Recv-Q Send-Q Local address/PSM Foreign address CID State c2afe900 0 0 00:02:72:00:d4:1a/3 00:07:e0:00:0b:ca 66 OPEN Active RFCOMM sessions L2PCB PCB Flag MTU Out-Q DLCs State c2afe900 c2b53380 1 127 0 Yes OPEN Active RFCOMM sockets PCB Recv-Q Send-Q Local address Foreign address Chan DLCI State c2e8bc80 0 250 00:02:72:00:d4:1a 00:07:e0:00:0b:ca 3 6 OPEN .... ==== Radio Frequency Communication (RFCOMM) The RFCOMM protocol provides emulation of serial ports over the L2CAP protocol. RFCOMM is a simple transport protocol, with additional provisions for emulating the 9 circuits of RS-232 (EIATIA-232-E) serial ports. It supports up to 60 simultaneous connections (RFCOMM channels) between two Bluetooth devices. For the purposes of RFCOMM, a complete communication path involves two applications running on the communication endpoints with a communication segment between them. RFCOMM is intended to cover applications that make use of the serial ports of the devices in which they reside. The communication segment is a direct connect Bluetooth link from one device to another. RFCOMM is only concerned with the connection between the devices in the direct connect case, or between the device and a modem in the network case. RFCOMM can support other configurations, such as modules that communicate via Bluetooth wireless technology on one side and provide a wired interface on the other side. In FreeBSD, RFCOMM is implemented at the Bluetooth sockets layer. ==== Service Discovery Protocol (SDP) The Service Discovery Protocol (SDP) provides the means for client applications to discover the existence of services provided by server applications as well as the attributes of those services. The attributes of a service include the type or class of service offered and the mechanism or protocol information needed to utilize the service. SDP involves communication between a SDP server and a SDP client. The server maintains a list of service records that describe the characteristics of services associated with the server. Each service record contains information about a single service. A client may retrieve information from a service record maintained by the SDP server by issuing a SDP request. If the client, or an application associated with the client, decides to use a service, it must open a separate connection to the service provider in order to utilize the service. SDP provides a mechanism for discovering services and their attributes, but it does not provide a mechanism for utilizing those services. Normally, a SDP client searches for services based on some desired characteristics of the services. However, there are times when it is desirable to discover which types of services are described by an SDP server's service records without any prior information about the services. This process of looking for any offered services is called _browsing_. The Bluetooth SDP server, man:sdpd[8], and command line client, man:sdpcontrol[8], are included in the standard FreeBSD installation. The following example shows how to perform a SDP browse query. [source,shell] .... % sdpcontrol -a 00:01:03:fc:6e:ec browse Record Handle: 00000000 Service Class ID List: Service Discovery Server (0x1000) Protocol Descriptor List: L2CAP (0x0100) Protocol specific parameter #1: u/int/uuid16 1 Protocol specific parameter #2: u/int/uuid16 1 Record Handle: 0x00000001 Service Class ID List: Browse Group Descriptor (0x1001) Record Handle: 0x00000002 Service Class ID List: LAN Access Using PPP (0x1102) Protocol Descriptor List: L2CAP (0x0100) RFCOMM (0x0003) Protocol specific parameter #1: u/int8/bool 1 Bluetooth Profile Descriptor List: LAN Access Using PPP (0x1102) ver. 1.0 .... Note that each service has a list of attributes, such as the RFCOMM channel. Depending on the service, the user might need to make note of some of the attributes. Some Bluetooth implementations do not support service browsing and may return an empty list. In this case, it is possible to search for the specific service. The example below shows how to search for the OBEX Object Push (OPUSH) service: [source,shell] .... % sdpcontrol -a 00:01:03:fc:6e:ec search OPUSH .... Offering services on FreeBSD to Bluetooth clients is done with the man:sdpd[8] server. The following line can be added to [.filename]#/etc/rc.conf#: [.programlisting] .... sdpd_enable="YES" .... Then the man:sdpd[8] daemon can be started with: [source,shell] .... # service sdpd start .... The local server application that wants to provide a Bluetooth service to remote clients will register the service with the local SDP daemon. An example of such an application is man:rfcomm_pppd[8]. Once started, it will register the Bluetooth LAN service with the local SDP daemon. The list of services registered with the local SDP server can be obtained by issuing a SDP browse query via the local control channel: [source,shell] .... # sdpcontrol -l browse .... ==== OBEX Object Push (OPUSH) Object Exchange (OBEX) is a widely used protocol for simple file transfers between mobile devices. Its main use is in infrared communication, where it is used for generic file transfers between notebooks or PDAs, and for sending business cards or calendar entries between cellular phones and other devices with Personal Information Manager (PIM) applications. The OBEX server and client are implemented by obexapp, which can be installed using the package:comms/obexapp[] package or port. The OBEX client is used to push and/or pull objects from the OBEX server. An example object is a business card or an appointment. The OBEX client can obtain the RFCOMM channel number from the remote device via SDP. This can be done by specifying the service name instead of the RFCOMM channel number. Supported service names are: `IrMC`, `FTRN`, and `OPUSH`. It is also possible to specify the RFCOMM channel as a number. Below is an example of an OBEX session where the device information object is pulled from the cellular phone, and a new object, the business card, is pushed into the phone's directory. [source,shell] .... % obexapp -a 00:80:37:29:19:a4 -C IrMC obex> get telecom/devinfo.txt devinfo-t39.txt Success, response: OK, Success (0x20) obex> put new.vcf Success, response: OK, Success (0x20) obex> di Success, response: OK, Success (0x20) .... In order to provide the OPUSH service, man:sdpd[8] must be running and a root folder, where all incoming objects will be stored, must be created. The default path to the root folder is [.filename]#/var/spool/obex#. Finally, start the OBEX server on a valid RFCOMM channel number. The OBEX server will automatically register the OPUSH service with the local SDP daemon. The example below shows how to start the OBEX server. [source,shell] .... # obexapp -s -C 10 .... ==== Serial Port Profile (SPP) The Serial Port Profile (SPP) allows Bluetooth devices to perform serial cable emulation. This profile allows legacy applications to use Bluetooth as a cable replacement, through a virtual serial port abstraction. In FreeBSD, man:rfcomm_sppd[1] implements SPP and a pseudo tty is used as a virtual serial port abstraction. The example below shows how to connect to a remote device's serial port service. A RFCOMM channel does not have to be specified as man:rfcomm_sppd[1] can obtain it from the remote device via SDP. To override this, specify a RFCOMM channel on the command line. [source,shell] .... # rfcomm_sppd -a 00:07:E0:00:0B:CA -t rfcomm_sppd[94692]: Starting on /dev/pts/6... /dev/pts/6 .... Once connected, the pseudo tty can be used as serial port: [source,shell] .... # cu -l /dev/pts/6 .... The pseudo tty is printed on stdout and can be read by wrapper scripts: [.programlisting] .... PTS=`rfcomm_sppd -a 00:07:E0:00:0B:CA -t` cu -l $PTS .... === Troubleshooting By default, when FreeBSD is accepting a new connection, it tries to perform a role switch and become master. Some older Bluetooth devices which do not support role switching will not be able to connect. Since role switching is performed when a new connection is being established, it is not possible to ask the remote device if it supports role switching. However, there is a HCI option to disable role switching on the local side: [source,shell] .... # hccontrol -n ubt0hci write_node_role_switch 0 .... To display Bluetooth packets, use the third-party package hcidump, which can be installed using the package:comms/hcidump[] package or port. This utility is similar to man:tcpdump[1] and can be used to display the contents of Bluetooth packets on the terminal and to dump the Bluetooth packets to a file. [[network-bridging]] == Bridging It is sometimes useful to divide a network, such as an Ethernet segment, into network segments without having to create IP subnets and use a router to connect the segments together. A device that connects two networks together in this fashion is called a "bridge". A bridge works by learning the MAC addresses of the devices on each of its network interfaces. It forwards traffic between networks only when the source and destination MAC addresses are on different networks. In many respects, a bridge is like an Ethernet switch with very few ports. A FreeBSD system with multiple network interfaces can be configured to act as a bridge. Bridging can be useful in the following situations: Connecting Networks:: The basic operation of a bridge is to join two or more network segments. There are many reasons to use a host-based bridge instead of networking equipment, such as cabling constraints or firewalling. A bridge can also connect a wireless interface running in hostap mode to a wired network and act as an access point. Filtering/Traffic Shaping Firewall:: A bridge can be used when firewall functionality is needed without routing or Network Address Translation (NAT). + An example is a small company that is connected via DSL or ISDN to an ISP. There are thirteen public IP addresses from the ISP and ten computers on the network. In this situation, using a router-based firewall is difficult because of subnetting issues. A bridge-based firewall can be configured without any IP addressing issues. Network Tap:: A bridge can join two network segments in order to inspect all Ethernet frames that pass between them using man:bpf[4] and man:tcpdump[1] on the bridge interface or by sending a copy of all frames out an additional interface known as a span port. Layer 2 VPN:: Two Ethernet networks can be joined across an IP link by bridging the networks to an EtherIP tunnel or a man:tap[4] based solution such as OpenVPN. Layer 2 Redundancy:: A network can be connected together with multiple links and use the Spanning Tree Protocol (STP) to block redundant paths. This section describes how to configure a FreeBSD system as a bridge using man:if_bridge[4]. A netgraph bridging driver is also available, and is described in man:ng_bridge[4]. [NOTE] ==== Packet filtering can be used with any firewall package that hooks into the man:pfil[9] framework. The bridge can be used as a traffic shaper with man:altq[4] or man:dummynet[4]. ==== === Enabling the Bridge In FreeBSD, man:if_bridge[4] is a kernel module which is automatically loaded by man:ifconfig[8] when creating a bridge interface. It is also possible to compile bridge support into a custom kernel by adding `device if_bridge` to the custom kernel configuration file. The bridge is created using interface cloning. To create the bridge interface: [source,shell] .... # ifconfig bridge create bridge0 # ifconfig bridge0 bridge0: flags=8802 metric 0 mtu 1500 ether 96:3d:4b:f1:79:7a id 00:00:00:00:00:00 priority 32768 hellotime 2 fwddelay 15 maxage 20 holdcnt 6 proto rstp maxaddr 100 timeout 1200 root id 00:00:00:00:00:00 priority 0 ifcost 0 port 0 .... When a bridge interface is created, it is automatically assigned a randomly generated Ethernet address. The `maxaddr` and `timeout` parameters control how many MAC addresses the bridge will keep in its forwarding table and how many seconds before each entry is removed after it is last seen. The other parameters control how STP operates. Next, specify which network interfaces to add as members of the bridge. For the bridge to forward packets, all member interfaces and the bridge need to be up: [source,shell] .... # ifconfig bridge0 addm fxp0 addm fxp1 up # ifconfig fxp0 up # ifconfig fxp1 up .... The bridge can now forward Ethernet frames between [.filename]#fxp0# and [.filename]#fxp1#. Add the following lines to [.filename]#/etc/rc.conf# so the bridge is created at startup: [.programlisting] .... cloned_interfaces="bridge0" ifconfig_bridge0="addm fxp0 addm fxp1 up" ifconfig_fxp0="up" ifconfig_fxp1="up" .... If the bridge host needs an IP address, set it on the bridge interface, not on the member interfaces. The address can be set statically or via DHCP. This example sets a static IP address: [source,shell] .... # ifconfig bridge0 inet 192.168.0.1/24 .... It is also possible to assign an IPv6 address to a bridge interface. To make the changes permanent, add the addressing information to [.filename]#/etc/rc.conf#. [NOTE] ==== When packet filtering is enabled, bridged packets will pass through the filter inbound on the originating interface on the bridge interface, and outbound on the appropriate interfaces. Either stage can be disabled. When direction of the packet flow is important, it is best to firewall on the member interfaces rather than the bridge itself. The bridge has several configurable settings for passing non-IP and IP packets, and layer2 firewalling with man:ipfw[8]. See man:if_bridge[4] for more information. ==== === Enabling Spanning Tree For an Ethernet network to function properly, only one active path can exist between two devices. The STP protocol detects loops and puts redundant links into a blocked state. Should one of the active links fail, STP calculates a different tree and enables one of the blocked paths to restore connectivity to all points in the network. The Rapid Spanning Tree Protocol (RSTP or 802.1w) provides backwards compatibility with legacy STP. RSTP provides faster convergence and exchanges information with neighboring switches to quickly transition to forwarding mode without creating loops. FreeBSD supports RSTP and STP as operating modes, with RSTP being the default mode. STP can be enabled on member interfaces using man:ifconfig[8]. For a bridge with [.filename]#fxp0# and [.filename]#fxp1# as the current interfaces, enable STP with: [source,shell] .... # ifconfig bridge0 stp fxp0 stp fxp1 bridge0: flags=8843 metric 0 mtu 1500 ether d6:cf:d5:a0:94:6d id 00:01:02:4b:d4:50 priority 32768 hellotime 2 fwddelay 15 maxage 20 holdcnt 6 proto rstp maxaddr 100 timeout 1200 root id 00:01:02:4b:d4:50 priority 32768 ifcost 0 port 0 member: fxp0 flags=1c7 port 3 priority 128 path cost 200000 proto rstp role designated state forwarding member: fxp1 flags=1c7 port 4 priority 128 path cost 200000 proto rstp role designated state forwarding .... This bridge has a spanning tree ID of `00:01:02:4b:d4:50` and a priority of `32768`. As the `root id` is the same, it indicates that this is the root bridge for the tree. Another bridge on the network also has STP enabled: [source,shell] .... bridge0: flags=8843 metric 0 mtu 1500 ether 96:3d:4b:f1:79:7a id 00:13:d4:9a:06:7a priority 32768 hellotime 2 fwddelay 15 maxage 20 holdcnt 6 proto rstp maxaddr 100 timeout 1200 root id 00:01:02:4b:d4:50 priority 32768 ifcost 400000 port 4 member: fxp0 flags=1c7 port 4 priority 128 path cost 200000 proto rstp role root state forwarding member: fxp1 flags=1c7 port 5 priority 128 path cost 200000 proto rstp role designated state forwarding .... The line `root id 00:01:02:4b:d4:50 priority 32768 ifcost 400000 port 4` shows that the root bridge is `00:01:02:4b:d4:50` and has a path cost of `400000` from this bridge. The path to the root bridge is via `port 4` which is [.filename]#fxp0#. === Bridge Interface Parameters Several `ifconfig` parameters are unique to bridge interfaces. This section summarizes some common uses for these parameters. The complete list of available parameters is described in man:ifconfig[8]. private:: A private interface does not forward any traffic to any other port that is also designated as a private interface. The traffic is blocked unconditionally so no Ethernet frames will be forwarded, including ARP packets. If traffic needs to be selectively blocked, a firewall should be used instead. span:: A span port transmits a copy of every Ethernet frame received by the bridge. The number of span ports configured on a bridge is unlimited, but if an interface is designated as a span port, it cannot also be used as a regular bridge port. This is most useful for snooping a bridged network passively on another host connected to one of the span ports of the bridge. For example, to send a copy of all frames out the interface named [.filename]#fxp4#: + [source,shell] .... # ifconfig bridge0 span fxp4 .... sticky:: If a bridge member interface is marked as sticky, dynamically learned address entries are treated as static entries in the forwarding cache. Sticky entries are never aged out of the cache or replaced, even if the address is seen on a different interface. This gives the benefit of static address entries without the need to pre-populate the forwarding table. Clients learned on a particular segment of the bridge cannot roam to another segment. + An example of using sticky addresses is to combine the bridge with VLANs in order to isolate customer networks without wasting IP address space. Consider that `CustomerA` is on `vlan100`, `CustomerB` is on `vlan101`, and the bridge has the address `192.168.0.1`: + [source,shell] .... # ifconfig bridge0 addm vlan100 sticky vlan100 addm vlan101 sticky vlan101 # ifconfig bridge0 inet 192.168.0.1/24 .... + In this example, both clients see `192.168.0.1` as their default gateway. Since the bridge cache is sticky, one host cannot spoof the MAC address of the other customer in order to intercept their traffic. + Any communication between the VLANs can be blocked using a firewall or, as seen in this example, private interfaces: + [source,shell] .... # ifconfig bridge0 private vlan100 private vlan101 .... + The customers are completely isolated from each other and the full `/24` address range can be allocated without subnetting. + The number of unique source MAC addresses behind an interface can be limited. Once the limit is reached, packets with unknown source addresses are dropped until an existing host cache entry expires or is removed. + The following example sets the maximum number of Ethernet devices for `CustomerA` on `vlan100` to 10: + [source,shell] .... # ifconfig bridge0 ifmaxaddr vlan100 10 .... Bridge interfaces also support monitor mode, where the packets are discarded after man:bpf[4] processing and are not processed or forwarded further. This can be used to multiplex the input of two or more interfaces into a single man:bpf[4] stream. This is useful for reconstructing the traffic for network taps that transmit the RX/TX signals out through two separate interfaces. For example, to read the input from four network interfaces as one stream: [source,shell] .... # ifconfig bridge0 addm fxp0 addm fxp1 addm fxp2 addm fxp3 monitor up # tcpdump -i bridge0 .... === SNMP Monitoring The bridge interface and STP parameters can be monitored via man:bsnmpd[1] which is included in the FreeBSD base system. The exported bridge MIBs conform to IETF standards so any SNMP client or monitoring package can be used to retrieve the data. To enable monitoring on the bridge, uncomment this line in [.filename]#/etc/snmpd.config# by removing the beginning `#` symbol: [.programlisting] .... begemotSnmpdModulePath."bridge" = "/usr/lib/snmp_bridge.so" .... Other configuration settings, such as community names and access lists, may need to be modified in this file. See man:bsnmpd[1] and man:snmp_bridge[3] for more information. Once these edits are saved, add this line to [.filename]#/etc/rc.conf#: [.programlisting] .... bsnmpd_enable="YES" .... Then, start man:bsnmpd[1]: [source,shell] .... # service bsnmpd start .... The following examples use the Net-SNMP software (package:net-mgmt/net-snmp[]) to query a bridge from a client system. The package:net-mgmt/bsnmptools[] port can also be used. From the SNMP client which is running Net-SNMP, add the following lines to [.filename]#$HOME/.snmp/snmp.conf# in order to import the bridge MIB definitions: [.programlisting] .... mibdirs +/usr/shared/snmp/mibs mibs +BRIDGE-MIB:RSTP-MIB:BEGEMOT-MIB:BEGEMOT-BRIDGE-MIB .... To monitor a single bridge using the IETF BRIDGE-MIB (RFC4188): [source,shell] .... % snmpwalk -v 2c -c public bridge1.example.com mib-2.dot1dBridge BRIDGE-MIB::dot1dBaseBridgeAddress.0 = STRING: 66:fb:9b:6e:5c:44 BRIDGE-MIB::dot1dBaseNumPorts.0 = INTEGER: 1 ports BRIDGE-MIB::dot1dStpTimeSinceTopologyChange.0 = Timeticks: (189959) 0:31:39.59 centi-seconds BRIDGE-MIB::dot1dStpTopChanges.0 = Counter32: 2 BRIDGE-MIB::dot1dStpDesignatedRoot.0 = Hex-STRING: 80 00 00 01 02 4B D4 50 ... BRIDGE-MIB::dot1dStpPortState.3 = INTEGER: forwarding(5) BRIDGE-MIB::dot1dStpPortEnable.3 = INTEGER: enabled(1) BRIDGE-MIB::dot1dStpPortPathCost.3 = INTEGER: 200000 BRIDGE-MIB::dot1dStpPortDesignatedRoot.3 = Hex-STRING: 80 00 00 01 02 4B D4 50 BRIDGE-MIB::dot1dStpPortDesignatedCost.3 = INTEGER: 0 BRIDGE-MIB::dot1dStpPortDesignatedBridge.3 = Hex-STRING: 80 00 00 01 02 4B D4 50 BRIDGE-MIB::dot1dStpPortDesignatedPort.3 = Hex-STRING: 03 80 BRIDGE-MIB::dot1dStpPortForwardTransitions.3 = Counter32: 1 RSTP-MIB::dot1dStpVersion.0 = INTEGER: rstp(2) .... The `dot1dStpTopChanges.0` value is two, indicating that the STP bridge topology has changed twice. A topology change means that one or more links in the network have changed or failed and a new tree has been calculated. The `dot1dStpTimeSinceTopologyChange.0` value will show when this happened. To monitor multiple bridge interfaces, the private BEGEMOT-BRIDGE-MIB can be used: [source,shell] .... % snmpwalk -v 2c -c public bridge1.example.com enterprises.fokus.begemot.begemotBridge BEGEMOT-BRIDGE-MIB::begemotBridgeBaseName."bridge0" = STRING: bridge0 BEGEMOT-BRIDGE-MIB::begemotBridgeBaseName."bridge2" = STRING: bridge2 BEGEMOT-BRIDGE-MIB::begemotBridgeBaseAddress."bridge0" = STRING: e:ce:3b:5a:9e:13 BEGEMOT-BRIDGE-MIB::begemotBridgeBaseAddress."bridge2" = STRING: 12:5e:4d:74:d:fc BEGEMOT-BRIDGE-MIB::begemotBridgeBaseNumPorts."bridge0" = INTEGER: 1 BEGEMOT-BRIDGE-MIB::begemotBridgeBaseNumPorts."bridge2" = INTEGER: 1 ... BEGEMOT-BRIDGE-MIB::begemotBridgeStpTimeSinceTopologyChange."bridge0" = Timeticks: (116927) 0:19:29.27 centi-seconds BEGEMOT-BRIDGE-MIB::begemotBridgeStpTimeSinceTopologyChange."bridge2" = Timeticks: (82773) 0:13:47.73 centi-seconds BEGEMOT-BRIDGE-MIB::begemotBridgeStpTopChanges."bridge0" = Counter32: 1 BEGEMOT-BRIDGE-MIB::begemotBridgeStpTopChanges."bridge2" = Counter32: 1 BEGEMOT-BRIDGE-MIB::begemotBridgeStpDesignatedRoot."bridge0" = Hex-STRING: 80 00 00 40 95 30 5E 31 BEGEMOT-BRIDGE-MIB::begemotBridgeStpDesignatedRoot."bridge2" = Hex-STRING: 80 00 00 50 8B B8 C6 A9 .... To change the bridge interface being monitored via the `mib-2.dot1dBridge` subtree: [source,shell] .... % snmpset -v 2c -c private bridge1.example.com BEGEMOT-BRIDGE-MIB::begemotBridgeDefaultBridgeIf.0 s bridge2 .... [[network-aggregation]] == Link Aggregation and Failover FreeBSD provides the man:lagg[4] interface which can be used to aggregate multiple network interfaces into one virtual interface in order to provide failover and link aggregation. Failover allows traffic to continue to flow as long as at least one aggregated network interface has an established link. Link aggregation works best on switches which support LACP, as this protocol distributes traffic bi-directionally while responding to the failure of individual links. The aggregation protocols supported by the lagg interface determine which ports are used for outgoing traffic and whether or not a specific port accepts incoming traffic. The following protocols are supported by man:lagg[4]: failover:: This mode sends and receives traffic only through the master port. If the master port becomes unavailable, the next active port is used. The first interface added to the virtual interface is the master port and all subsequently added interfaces are used as failover devices. If failover to a non-master port occurs, the original port becomes master once it becomes available again. fec / loadbalance:: Cisco(R) Fast EtherChannel(R) (FEC) is found on older Cisco(R) switches. It provides a static setup and does not negotiate aggregation with the peer or exchange frames to monitor the link. If the switch supports LACP, that should be used instead. lacp:: The IEEE(R) 802.3ad Link Aggregation Control Protocol (LACP) negotiates a set of aggregable links with the peer into one or more Link Aggregated Groups (LAGs). Each LAG is composed of ports of the same speed, set to full-duplex operation, and traffic is balanced across the ports in the LAG with the greatest total speed. Typically, there is only one LAG which contains all the ports. In the event of changes in physical connectivity, LACP will quickly converge to a new configuration. + LACP balances outgoing traffic across the active ports based on hashed protocol header information and accepts incoming traffic from any active port. The hash includes the Ethernet source and destination address and, if available, the VLAN tag, and the IPv4 or IPv6 source and destination address. roundrobin:: This mode distributes outgoing traffic using a round-robin scheduler through all active ports and accepts incoming traffic from any active port. Since this mode violates Ethernet frame ordering, it should be used with caution. === Configuration Examples This section demonstrates how to configure a Cisco(R) switch and a FreeBSD system for LACP load balancing. It then shows how to configure two Ethernet interfaces in failover mode as well as how to configure failover mode between an Ethernet and a wireless interface. [[networking-lacp-aggregation-cisco]] .LACP Aggregation with a Cisco(R) Switch [example] ==== This example connects two man:fxp[4] Ethernet interfaces on a FreeBSD machine to the first two Ethernet ports on a Cisco(R) switch as a single load balanced and fault tolerant link. More interfaces can be added to increase throughput and fault tolerance. Replace the names of the Cisco(R) ports, Ethernet devices, channel group number, and IP address shown in the example to match the local configuration. Frame ordering is mandatory on Ethernet links and any traffic between two stations always flows over the same physical link, limiting the maximum speed to that of one interface. The transmit algorithm attempts to use as much information as it can to distinguish different traffic flows and balance the flows across the available interfaces. On the Cisco(R) switch, add the _FastEthernet0/1_ and _FastEthernet0/2_ interfaces to channel group _1_: [source,shell] .... interface FastEthernet0/1 channel-group 1 mode active channel-protocol lacp ! interface FastEthernet0/2 channel-group 1 mode active channel-protocol lacp .... On the FreeBSD system, create the man:lagg[4] interface using the physical interfaces _fxp0_ and _fxp1_ and bring the interfaces up with an IP address of _10.0.0.3/24_: [source,shell] .... # ifconfig fxp0 up # ifconfig fxp1 up # ifconfig lagg0 create # ifconfig lagg0 up laggproto lacp laggport fxp0 laggport fxp1 10.0.0.3/24 .... Next, verify the status of the virtual interface: [source,shell] .... # ifconfig lagg0 lagg0: flags=8843 metric 0 mtu 1500 options=8 ether 00:05:5d:71:8d:b8 inet 10.0.0.3 netmask 0xffffff00 broadcast 10.0.0.255 media: Ethernet autoselect status: active laggproto lacp laggport: fxp1 flags=1c laggport: fxp0 flags=1c .... Ports marked as `ACTIVE` are part of the LAG that has been negotiated with the remote switch. Traffic will be transmitted and received through these active ports. Add `-v` to the above command to view the LAG identifiers. To see the port status on the Cisco(R) switch: [source,shell] .... switch# show lacp neighbor Flags: S - Device is requesting Slow LACPDUs F - Device is requesting Fast LACPDUs A - Device is in Active mode P - Device is in Passive mode Channel group 1 neighbors Partner's information: LACP port Oper Port Port Port Flags Priority Dev ID Age Key Number State Fa0/1 SA 32768 0005.5d71.8db8 29s 0x146 0x3 0x3D Fa0/2 SA 32768 0005.5d71.8db8 29s 0x146 0x4 0x3D .... For more detail, type `show lacp neighbor detail`. To retain this configuration across reboots, add the following entries to [.filename]#/etc/rc.conf# on the FreeBSD system: [.programlisting] .... ifconfig_fxp0="up" ifconfig_fxp1="up" cloned_interfaces="lagg0" ifconfig_lagg0="laggproto lacp laggport fxp0 laggport fxp1 10.0.0.3/24" .... ==== [[networking-lagg-failover]] .Failover Mode [example] ==== Failover mode can be used to switch over to a secondary interface if the link is lost on the master interface. To configure failover, make sure that the underlying physical interfaces are up, then create the man:lagg[4] interface. In this example, _fxp0_ is the master interface, _fxp1_ is the secondary interface, and the virtual interface is assigned an IP address of _10.0.0.15/24_: [source,shell] .... # ifconfig fxp0 up # ifconfig fxp1 up # ifconfig lagg0 create # ifconfig lagg0 up laggproto failover laggport fxp0 laggport fxp1 10.0.0.15/24 .... The virtual interface should look something like this: [source,shell] .... # ifconfig lagg0 lagg0: flags=8843 metric 0 mtu 1500 options=8 ether 00:05:5d:71:8d:b8 inet 10.0.0.15 netmask 0xffffff00 broadcast 10.0.0.255 media: Ethernet autoselect status: active laggproto failover laggport: fxp1 flags=0<> laggport: fxp0 flags=5 .... Traffic will be transmitted and received on _fxp0_. If the link is lost on _fxp0_, _fxp1_ will become the active link. If the link is restored on the master interface, it will once again become the active link. To retain this configuration across reboots, add the following entries to [.filename]#/etc/rc.conf#: [.programlisting] .... ifconfig_fxp0="up" ifconfig_fxp1="up" cloned_interfaces="lagg0" ifconfig_lagg0="laggproto failover laggport fxp0 laggport fxp1 10.0.0.15/24" .... ==== [[networking-lagg-wired-and-wireless]] .Failover Mode Between Ethernet and Wireless Interfaces [example] ==== For laptop users, it is usually desirable to configure the wireless device as a secondary which is only used when the Ethernet connection is not available. With man:lagg[4], it is possible to configure a failover which prefers the Ethernet connection for both performance and security reasons, while maintaining the ability to transfer data over the wireless connection. This is achieved by overriding the Ethernet interface's MAC address with that of the wireless interface. [NOTE] -**** +==== In theory, either the Ethernet or wireless MAC address can be changed to match the other. However, some popular wireless interfaces lack support for overriding the MAC address. We therefore recommend overriding the Ethernet MAC address for this purpose. -**** +==== [NOTE] -**** +==== If the driver for the wireless interface is not loaded in the `GENERIC` or custom kernel, and the computer is running FreeBSD {rel121-current}, load the corresponding [.filename]#.ko# in [.filename]#/boot/loader.conf# by adding `*driver_load="YES"*` to that file and rebooting. Another, better way is to load the driver in [.filename]#/etc/rc.conf# by adding it to `kld_list` (see man:rc.conf[5] for details) in that file and rebooting. This is needed because otherwise the driver is not loaded yet at the time the man:lagg[4] interface is set up. -**** +==== In this example, the Ethernet interface, _re0_, is the master and the wireless interface, _wlan0_, is the failover. The _wlan0_ interface was created from the _ath0_ physical wireless interface, and the Ethernet interface will be configured with the MAC address of the wireless interface. First, determine the MAC address of the wireless interface: [source,shell] .... # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether b8:ee:65:5b:32:59 groups: wlan ssid Bbox-A3BD2403 channel 6 (2437 MHz 11g ht/20) bssid 00:37:b7:56:4b:60 regdomain ETSI country FR indoor ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 2:128-bit txpower 30 bmiss 7 scanvalid 60 protmode CTS ampdulimit 64k ampdudensity 8 shortgi -stbctx stbcrx -ldpc wme burst roaming MANUAL media: IEEE 802.11 Wireless Ethernet MCS mode 11ng status: associated nd6 options=29 .... Replace _wlan0_ to match the system's wireless interface name. The `ether` line will contain the MAC address of the specified interface. Now, change the MAC address of the Ethernet interface: [source,shell] .... # ifconfig re0 ether b8:ee:65:5b:32:59 .... Bring the wireless interface up (replacing _FR_ with your own 2-letter country code), but do not set an IP address: [source,shell] .... # ifconfig wlan0 create wlandev ath0 country FR ssid my_router up .... Make sure the _re0_ interface is up, then create the man:lagg[4] interface with _re0_ as master with failover to _wlan0_: [source,shell] .... # ifconfig re0 up # ifconfig lagg0 create # ifconfig lagg0 up laggproto failover laggport re0 laggport wlan0 .... The virtual interface should look something like this: [source,shell] .... # ifconfig lagg0 lagg0: flags=8843 metric 0 mtu 1500 options=8 ether b8:ee:65:5b:32:59 laggproto failover lagghash l2,l3,l4 laggport: re0 flags=5 laggport: wlan0 flags=0<> groups: lagg media: Ethernet autoselect status: active .... Then, start the DHCP client to obtain an IP address: [source,shell] .... # dhclient lagg0 .... To retain this configuration across reboots, add the following entries to [.filename]#/etc/rc.conf#: [.programlisting] .... ifconfig_re0="ether b8:ee:65:5b:32:59" wlans_ath0="wlan0" ifconfig_wlan0="WPA" create_args_wlan0="country FR" cloned_interfaces="lagg0" ifconfig_lagg0="up laggproto failover laggport re0 laggport wlan0 DHCP" .... ==== [[network-diskless]] == Diskless Operation with PXE The Intel(R) Preboot eXecution Environment (PXE) allows an operating system to boot over the network. For example, a FreeBSD system can boot over the network and operate without a local disk, using file systems mounted from an NFS server. PXE support is usually available in the BIOS. To use PXE when the machine starts, select the `Boot from network` option in the BIOS setup or type a function key during system initialization. In order to provide the files needed for an operating system to boot over the network, a PXE setup also requires properly configured DHCP, TFTP, and NFS servers, where: * Initial parameters, such as an IP address, executable boot filename and location, server name, and root path are obtained from the DHCP server. * The operating system loader file is booted using TFTP. * The file systems are loaded using NFS. When a computer PXE boots, it receives information over DHCP about where to obtain the initial boot loader file. After the host computer receives this information, it downloads the boot loader via TFTP and then executes the boot loader. In FreeBSD, the boot loader file is [.filename]#/boot/pxeboot#. After [.filename]#/boot/pxeboot# executes, the FreeBSD kernel is loaded and the rest of the FreeBSD bootup sequence proceeds, as described in crossref:boot[boot,The FreeBSD Booting Process]. This section describes how to configure these services on a FreeBSD system so that other systems can PXE boot into FreeBSD. Refer to man:diskless[8] for more information. [CAUTION] ==== As described, the system providing these services is insecure. It should live in a protected area of a network and be untrusted by other hosts. ==== [[network-pxe-nfs]] === Setting Up the PXE Environment The steps shown in this section configure the built-in NFS and TFTP servers. The next section demonstrates how to install and configure the DHCP server. In this example, the directory which will contain the files used by PXE users is [.filename]#/b/tftpboot/FreeBSD/install#. It is important that this directory exists and that the same directory name is set in both [.filename]#/etc/inetd.conf# and [.filename]#/usr/local/etc/dhcpd.conf#. [.procedure] . Create the root directory which will contain a FreeBSD installation to be NFS mounted: + [source,shell] .... # export NFSROOTDIR=/b/tftpboot/FreeBSD/install # mkdir -p ${NFSROOTDIR} .... . Enable the NFS server by adding this line to [.filename]#/etc/rc.conf#: + [.programlisting] .... nfs_server_enable="YES" .... . Export the diskless root directory via NFS by adding the following to [.filename]#/etc/exports#: + [.programlisting] .... /b -ro -alldirs -maproot=root .... . Start the NFS server: + [source,shell] .... # service nfsd start .... . Enable man:inetd[8] by adding the following line to [.filename]#/etc/rc.conf#: + [.programlisting] .... inetd_enable="YES" .... . Uncomment the following line in [.filename]#/etc/inetd.conf# by making sure it does not start with a `#` symbol: + [.programlisting] .... tftp dgram udp wait root /usr/libexec/tftpd tftpd -l -s /b/tftpboot .... + [NOTE] ==== Some PXE versions require the TCP version of TFTP. In this case, uncomment the second `tftp` line which contains `stream tcp`. ==== . Start man:inetd[8]: + [source,shell] .... # service inetd start .... . Install the base system into [.filename]#${NFSROOTDIR}#, either by decompressing the official archives or by rebuilding the FreeBSD kernel and userland (refer to crossref:cutting-edge[makeworld,“Updating FreeBSD from Source”] for more detailed instructions, but do not forget to add `DESTDIR=_${NFSROOTDIR}_` when running the `make installkernel` and `make installworld` commands. . Test that the TFTP server works and can download the boot loader which will be obtained via PXE: + [source,shell] .... # tftp localhost tftp> get FreeBSD/install/boot/pxeboot Received 264951 bytes in 0.1 seconds .... . Edit [.filename]#${NFSROOTDIR}/etc/fstab# and create an entry to mount the root file system over NFS: + [.programlisting] .... # Device Mountpoint FSType Options Dump Pass myhost.example.com:/b/tftpboot/FreeBSD/install / nfs ro 0 0 .... -+ ++ Replace _myhost.example.com_ with the hostname or IP address of the NFS server. In this example, the root file system is mounted read-only in order to prevent NFS clients from potentially deleting the contents of the root file system. . Set the root password in the PXE environment for client machines which are PXE booting : + [source,shell] .... # chroot ${NFSROOTDIR} # passwd .... . If needed, enable man:ssh[1] root logins for client machines which are PXE booting by editing [.filename]#${NFSROOTDIR}/etc/ssh/sshd_config# and enabling `PermitRootLogin`. This option is documented in man:sshd_config[5]. . Perform any other needed customizations of the PXE environment in [.filename]#${NFSROOTDIR}#. These customizations could include things like installing packages or editing the password file with man:vipw[8]. When booting from an NFS root volume, [.filename]#/etc/rc# detects the NFS boot and runs [.filename]#/etc/rc.initdiskless#. In this case, [.filename]#/etc# and [.filename]#/var# need to be memory backed file systems so that these directories are writable but the NFS root directory is read-only: [source,shell] .... # chroot ${NFSROOTDIR} # mkdir -p conf/base # tar -c -v -f conf/base/etc.cpio.gz --format cpio --gzip etc # tar -c -v -f conf/base/var.cpio.gz --format cpio --gzip var .... When the system boots, memory file systems for [.filename]#/etc# and [.filename]#/var# will be created and mounted and the contents of the [.filename]#cpio.gz# files will be copied into them. By default, these file systems have a maximum capacity of 5 megabytes. If your archives do not fit, which is usually the case for [.filename]#/var# when binary packages have been installed, request a larger size by putting the number of 512 byte sectors needed (e.g., 5 megabytes is 10240 sectors) in [.filename]#${NFSROOTDIR}/conf/base/etc/md_size# and [.filename]#${NFSROOTDIR}/conf/base/var/md_size# files for [.filename]#/etc# and [.filename]#/var# file systems respectively. [[network-pxe-setting-up-dhcp]] === Configuring the DHCP Server The DHCP server does not need to be the same machine as the TFTP and NFS server, but it needs to be accessible in the network. DHCP is not part of the FreeBSD base system but can be installed using the package:net/isc-dhcp44-server[] port or package. Once installed, edit the configuration file, [.filename]#/usr/local/etc/dhcpd.conf#. Configure the `next-server`, `filename`, and `root-path` settings as seen in this example: [.programlisting] .... subnet 192.168.0.0 netmask 255.255.255.0 { range 192.168.0.2 192.168.0.3 ; option subnet-mask 255.255.255.0 ; option routers 192.168.0.1 ; option broadcast-address 192.168.0.255 ; option domain-name-servers 192.168.35.35, 192.168.35.36 ; option domain-name "example.com"; # IP address of TFTP server next-server 192.168.0.1 ; # path of boot loader obtained via tftp filename "FreeBSD/install/boot/pxeboot" ; # pxeboot boot loader will try to NFS mount this directory for root FS option root-path "192.168.0.1:/b/tftpboot/FreeBSD/install/" ; } .... The `next-server` directive is used to specify the IP address of the TFTP server. The `filename` directive defines the path to [.filename]#/boot/pxeboot#. A relative filename is used, meaning that [.filename]#/b/tftpboot# is not included in the path. The `root-path` option defines the path to the NFS root file system. Once the edits are saved, enable DHCP at boot time by adding the following line to [.filename]#/etc/rc.conf#: [.programlisting] .... dhcpd_enable="YES" .... Then start the DHCP service: [source,shell] .... # service isc-dhcpd start .... === Debugging PXE Problems Once all of the services are configured and started, PXE clients should be able to automatically load FreeBSD over the network. If a particular client is unable to connect, when that client machine boots up, enter the BIOS configuration menu and confirm that it is set to boot from the network. This section describes some troubleshooting tips for isolating the source of the configuration problem should no clients be able to PXE boot. [.procedure] **** . Use the package:net/wireshark[] package or port to debug the network traffic involved during the PXE booting process, which is illustrated in the diagram below. + .PXE Booting Process with NFS Root Mount image::pxe-nfs.png[] + 1. Client broadcasts a DHCPDISCOVER message. + 2. The DHCP server responds with the IP address, next-server, filename, and root-path values. + 3. The client sends a TFTP request to next-server, asking to retrieve filename. + 4. The TFTP server responds and sends filename to client. + 5. The client executes filename, which is pxeboot(8), which then loads the kernel. When the kernel executes, the root file system specified by root-path is mounted over NFS. + . On the TFTP server, read [.filename]#/var/log/xferlog# to ensure that [.filename]#pxeboot# is being retrieved from the correct location. To test this example configuration: + [source,shell] .... # tftp 192.168.0.1 tftp> get FreeBSD/install/boot/pxeboot Received 264951 bytes in 0.1 seconds .... -+ ++ The `BUGS` sections in man:tftpd[8] and man:tftp[1] document some limitations with TFTP. . Make sure that the root file system can be mounted via NFS. To test this example configuration: + [source,shell] .... # mount -t nfs 192.168.0.1:/b/tftpboot/FreeBSD/install /mnt .... **** [[network-ipv6]] == IPv6 IPv6 is the new version of the well known IP protocol, also known as IPv4. IPv6 provides several advantages over IPv4 as well as many new features: * Its 128-bit address space allows for 340,282,366,920,938,463,463,374,607,431,768,211,456 addresses. This addresses the IPv4 address shortage and eventual IPv4 address exhaustion. * Routers only store network aggregation addresses in their routing tables, thus reducing the average space of a routing table to 8192 entries. This addresses the scalability issues associated with IPv4, which required every allocated block of IPv4 addresses to be exchanged between Internet routers, causing their routing tables to become too large to allow efficient routing. * Address autoconfiguration (http://www.ietf.org/rfc/rfc2462.txt[RFC2462]). * Mandatory multicast addresses. * Built-in IPsec (IP security). * Simplified header structure. * Support for mobile IP. * IPv6-to-IPv4 transition mechanisms. FreeBSD includes the http://www.kame.net/[http://www.kame.net/] IPv6 reference implementation and comes with everything needed to use IPv6. This section focuses on getting IPv6 configured and running. === Background on IPv6 Addresses There are three different types of IPv6 addresses: Unicast:: A packet sent to a unicast address arrives at the interface belonging to the address. Anycast:: These addresses are syntactically indistinguishable from unicast addresses but they address a group of interfaces. The packet destined for an anycast address will arrive at the nearest router interface. Anycast addresses are only used by routers. Multicast:: These addresses identify a group of interfaces. A packet destined for a multicast address will arrive at all interfaces belonging to the multicast group. The IPv4 broadcast address, usually `xxx.xxx.xxx.255`, is expressed by multicast addresses in IPv6. When reading an IPv6 address, the canonical form is represented as `x:x:x:x:x:x:x:x`, where each `x` represents a 16 bit hex value. An example is `FEBC:A574:382B:23C1:AA49:4592:4EFE:9982`. Often, an address will have long substrings of all zeros. A `::` (double colon) can be used to replace one substring per address. Also, up to three leading ``0``s per hex value can be omitted. For example, `fe80::1` corresponds to the canonical form `fe80:0000:0000:0000:0000:0000:0000:0001`. A third form is to write the last 32 bits using the well known IPv4 notation. For example, `2002::10.0.0.1` corresponds to the hexadecimal canonical representation `2002:0000:0000:0000:0000:0000:0a00:0001`, which in turn is equivalent to `2002::a00:1`. To view a FreeBSD system's IPv6 address, use man:ifconfig[8]: [source,shell] .... # ifconfig .... [.programlisting] .... rl0: flags=8943 mtu 1500 inet 10.0.0.10 netmask 0xffffff00 broadcast 10.0.0.255 inet6 fe80::200:21ff:fe03:8e1%rl0 prefixlen 64 scopeid 0x1 ether 00:00:21:03:08:e1 media: Ethernet autoselect (100baseTX ) status: active .... In this example, the [.filename]#rl0# interface is using `fe80::200:21ff:fe03:8e1%rl0`, an auto-configured link-local address which was automatically generated from the MAC address. Some IPv6 addresses are reserved. A summary of these reserved addresses is seen in <>: [[reservedip6]] .Reserved IPv6 Addresses [cols="1,1,1,1", frame="none", options="header"] |=== | IPv6 address | Prefixlength (Bits) | Description | Notes |`::` |128 bits |unspecified |Equivalent to `0.0.0.0` in IPv4. |`::1` |128 bits |loopback address |Equivalent to `127.0.0.1` in IPv4. |`::00:xx:xx:xx:xx` |96 bits |embedded IPv4 |The lower 32 bits are the compatible IPv4 address. |`::ff:xx:xx:xx:xx` |96 bits |IPv4 mapped IPv6 address |The lower 32 bits are the IPv4 address for hosts which do not support IPv6. |`fe80::/10` |10 bits |link-local |Equivalent to 169.254.0.0/16 in IPv4. |`fc00::/7` |7 bits |unique-local |Unique local addresses are intended for local communication and are only routable within a set of cooperating sites. |`ff00::` |8 bits |multicast | |``2000::-3fff::`` |3 bits |global unicast |All global unicast addresses are assigned from this pool. The first 3 bits are `001`. |=== For further information on the structure of IPv6 addresses, refer to http://www.ietf.org/rfc/rfc3513.txt[RFC3513]. === Configuring IPv6 To configure a FreeBSD system as an IPv6 client, add these two lines to [.filename]#rc.conf#: [.programlisting] .... ifconfig_rl0_ipv6="inet6 accept_rtadv" rtsold_enable="YES" .... The first line enables the specified interface to receive router advertisement messages. The second line enables the router solicitation daemon, man:rtsol[8]. If the interface needs a statically assigned IPv6 address, add an entry to specify the static address and associated prefix length: [.programlisting] .... ifconfig_rl0_ipv6="inet6 2001:db8:4672:6565:2026:5043:2d42:5344 prefixlen 64" .... To assign a default router, specify its address: [.programlisting] .... ipv6_defaultrouter="2001:db8:4672:6565::1" .... === Connecting to a Provider In order to connect to other IPv6 networks, one must have a provider or a tunnel that supports IPv6: * Contact an Internet Service Provider to see if they offer IPv6. * http://www.tunnelbroker.net[Hurricane Electric] offers tunnels with end-points all around the globe. [NOTE] ==== Install the package:net/freenet6[] package or port for a dial-up connection. ==== This section demonstrates how to take the directions from a tunnel provider and convert them into [.filename]#/etc/rc.conf# settings that will persist through reboots. The first [.filename]#/etc/rc.conf# entry creates the generic tunneling interface [.filename]#gif0#: [.programlisting] .... cloned_interfaces="gif0" .... Next, configure that interface with the IPv4 addresses of the local and remote endpoints. Replace `_MY_IPv4_ADDR_` and `_REMOTE_IPv4_ADDR_` with the actual IPv4 addresses: [.programlisting] .... create_args_gif0="tunnel MY_IPv4_ADDR REMOTE_IPv4_ADDR" .... To apply the IPv6 address that has been assigned for use as the IPv6 tunnel endpoint, add this line, replacing `_MY_ASSIGNED_IPv6_TUNNEL_ENDPOINT_ADDR_` with the assigned address: [.programlisting] .... ifconfig_gif0_ipv6="inet6 MY_ASSIGNED_IPv6_TUNNEL_ENDPOINT_ADDR" .... Then, set the default route for the other side of the IPv6 tunnel. Replace `_MY_IPv6_REMOTE_TUNNEL_ENDPOINT_ADDR_` with the default gateway address assigned by the provider: [.programlisting] .... ipv6_defaultrouter="MY_IPv6_REMOTE_TUNNEL_ENDPOINT_ADDR" .... If the FreeBSD system will route IPv6 packets between the rest of the network and the world, enable the gateway using this line: [.programlisting] .... ipv6_gateway_enable="YES" .... === Router Advertisement and Host Auto Configuration This section demonstrates how to setup man:rtadvd[8] to advertise the IPv6 default route. To enable man:rtadvd[8], add the following to [.filename]#/etc/rc.conf#: [.programlisting] .... rtadvd_enable="YES" .... It is important to specify the interface on which to do IPv6 router advertisement. For example, to tell man:rtadvd[8] to use [.filename]#rl0#: [.programlisting] .... rtadvd_interfaces="rl0" .... Next, create the configuration file, [.filename]#/etc/rtadvd.conf# as seen in this example: [.programlisting] .... rl0:\ :addrs#1:addr="2001:db8:1f11:246::":prefixlen#64:tc=ether: .... Replace [.filename]#rl0# with the interface to be used and `2001:db8:1f11:246::` with the prefix of the allocation. For a dedicated `/64` subnet, nothing else needs to be changed. Otherwise, change the `prefixlen#` to the correct value. === IPv6 and IPv6 Address Mapping When IPv6 is enabled on a server, there may be a need to enable IPv4 mapped IPv6 address communication. This compatibility option allows for IPv4 addresses to be represented as IPv6 addresses. Permitting IPv6 applications to communicate with IPv4 and vice versa may be a security issue. This option may not be required in most cases and is available only for compatibility. This option will allow IPv6-only applications to work with IPv4 in a dual stack environment. This is most useful for third party applications which may not support an IPv6-only environment. To enable this feature, add the following to [.filename]#/etc/rc.conf#: [.programlisting] .... ipv6_ipv4mapping="YES" .... Reviewing the information in RFC 3493, section 3.6 and 3.7 as well as RFC 4038 section 4.2 may be useful to some administrators. [[carp]] == Common Address Redundancy Protocol (CARP) The Common Address Redundancy Protocol (CARP) allows multiple hosts to share the same IP address and Virtual Host ID (VHID) in order to provide _high availability_ for one or more services. This means that one or more hosts can fail, and the other hosts will transparently take over so that users do not see a service failure. In addition to the shared IP address, each host has its own IP address for management and configuration. All of the machines that share an IP address have the same VHID. The VHID for each virtual IP address must be unique across the broadcast domain of the network interface. High availability using CARP is built into FreeBSD, though the steps to configure it vary slightly depending upon the FreeBSD version. This section provides the same example configuration for versions before and equal to or after FreeBSD 10. This example configures failover support with three hosts, all with unique IP addresses, but providing the same web content. It has two different masters named `hosta.example.org` and `hostb.example.org`, with a shared backup named `hostc.example.org`. These machines are load balanced with a Round Robin DNS configuration. The master and backup machines are configured identically except for their hostnames and management IP addresses. These servers must have the same configuration and run the same services. When the failover occurs, requests to the service on the shared IP address can only be answered correctly if the backup server has access to the same content. The backup machine has two additional CARP interfaces, one for each of the master content server's IP addresses. When a failure occurs, the backup server will pick up the failed master machine's IP address. [[carp-10x]] === Using CARP on FreeBSD 10 and Later Enable boot-time support for CARP by adding an entry for the [.filename]#carp.ko# kernel module in [.filename]#/boot/loader.conf#: [.programlisting] .... carp_load="YES" .... To load the module now without rebooting: [source,shell] .... # kldload carp .... For users who prefer to use a custom kernel, include the following line in the custom kernel configuration file and compile the kernel as described in crossref:kernelconfig[kernelconfig,Configuring the FreeBSD Kernel]: [.programlisting] .... device carp .... The hostname, management IP address and subnet mask, shared IP address, and VHID are all set by adding entries to [.filename]#/etc/rc.conf#. This example is for `hosta.example.org`: [.programlisting] .... hostname="hosta.example.org" ifconfig_em0="inet 192.168.1.3 netmask 255.255.255.0" ifconfig_em0_alias0="inet vhid 1 pass testpass alias 192.168.1.50/32" .... The next set of entries are for `hostb.example.org`. Since it represents a second master, it uses a different shared IP address and VHID. However, the passwords specified with `pass` must be identical as CARP will only listen to and accept advertisements from machines with the correct password. [.programlisting] .... hostname="hostb.example.org" ifconfig_em0="inet 192.168.1.4 netmask 255.255.255.0" ifconfig_em0_alias0="inet vhid 2 pass testpass alias 192.168.1.51/32" .... The third machine, `hostc.example.org`, is configured to handle failover from either master. This machine is configured with two CARPVHIDs, one to handle the virtual IP address for each of the master hosts. The CARP advertising skew, `advskew`, is set to ensure that the backup host advertises later than the master, since `advskew` controls the order of precedence when there are multiple backup servers. [.programlisting] .... hostname="hostc.example.org" ifconfig_em0="inet 192.168.1.5 netmask 255.255.255.0" ifconfig_em0_alias0="inet vhid 1 advskew 100 pass testpass alias 192.168.1.50/32" ifconfig_em0_alias1="inet vhid 2 advskew 100 pass testpass alias 192.168.1.51/32" .... Having two CARPVHIDs configured means that `hostc.example.org` will notice if either of the master servers becomes unavailable. If a master fails to advertise before the backup server, the backup server will pick up the shared IP address until the master becomes available again. [NOTE] ==== If the original master server becomes available again, `hostc.example.org` will not release the virtual IP address back to it automatically. For this to happen, preemption has to be enabled. The feature is disabled by default, it is controlled via the man:sysctl[8] variable `net.inet.carp.preempt`. The administrator can force the backup server to return the IP address to the master: [source,shell] .... # ifconfig em0 vhid 1 state backup .... ==== Once the configuration is complete, either restart networking or reboot each system. High availability is now enabled. CARP functionality can be controlled via several man:sysctl[8] variables documented in the man:carp[4] manual pages. Other actions can be triggered from CARP events by using man:devd[8]. [[carp-9x]] === Using CARP on FreeBSD 9 and Earlier The configuration for these versions of FreeBSD is similar to the one described in the previous section, except that a CARP device must first be created and referred to in the configuration. Enable boot-time support for CARP by loading the [.filename]#if_carp.ko# kernel module in [.filename]#/boot/loader.conf#: [.programlisting] .... if_carp_load="YES" .... To load the module now without rebooting: [source,shell] .... # kldload carp .... For users who prefer to use a custom kernel, include the following line in the custom kernel configuration file and compile the kernel as described in crossref:kernelconfig[kernelconfig,Configuring the FreeBSD Kernel]: [.programlisting] .... device carp .... Next, on each host, create a CARP device: [source,shell] .... # ifconfig carp0 create .... Set the hostname, management IP address, the shared IP address, and VHID by adding the required lines to [.filename]#/etc/rc.conf#. Since a virtual CARP device is used instead of an alias, the actual subnet mask of `/24` is used instead of `/32`. Here are the entries for `hosta.example.org`: [.programlisting] .... hostname="hosta.example.org" ifconfig_fxp0="inet 192.168.1.3 netmask 255.255.255.0" cloned_interfaces="carp0" ifconfig_carp0="vhid 1 pass testpass 192.168.1.50/24" .... On `hostb.example.org`: [.programlisting] .... hostname="hostb.example.org" ifconfig_fxp0="inet 192.168.1.4 netmask 255.255.255.0" cloned_interfaces="carp0" ifconfig_carp0="vhid 2 pass testpass 192.168.1.51/24" .... The third machine, `hostc.example.org`, is configured to handle failover from either of the master hosts: [.programlisting] .... hostname="hostc.example.org" ifconfig_fxp0="inet 192.168.1.5 netmask 255.255.255.0" cloned_interfaces="carp0 carp1" ifconfig_carp0="vhid 1 advskew 100 pass testpass 192.168.1.50/24" ifconfig_carp1="vhid 2 advskew 100 pass testpass 192.168.1.51/24" .... [NOTE] ==== Preemption is disabled in the [.filename]#GENERIC# FreeBSD kernel. If preemption has been enabled with a custom kernel, `hostc.example.org` may not release the IP address back to the original content server. The administrator can force the backup server to return the IP address to the master with the command: [source,shell] .... # ifconfig carp0 down && ifconfig carp0 up .... This should be done on the [.filename]#carp# interface which corresponds to the correct host. ==== Once the configuration is complete, either restart networking or reboot each system. High availability is now enabled. [[network-vlan]] == VLANs VLANs are a way of virtually dividing up a network into many different subnetworks, also referred to as segmenting. Each segment will have its own broadcast domain and be isolated from other VLANs. On FreeBSD, VLANs must be supported by the network card driver. To see which drivers support vlans, refer to the man:vlan[4] manual page. When configuring a VLAN, a couple pieces of information must be known. First, which network interface? Second, what is the VLAN tag? To configure VLANs at run time, with a NIC of `em0` and a VLAN tag of `5` the command would look like this: [source,shell] .... # ifconfig em0.5 create vlan 5 vlandev em0 inet 192.168.20.20/24 .... [NOTE] ==== See how the interface name includes the NIC driver name and the VLAN tag, separated by a period? This is a best practice to make maintaining the VLAN configuration easy when many VLANs are present on a machine. ==== To configure VLANs at boot time, [.filename]#/etc/rc.conf# must be updated. To duplicate the configuration above, the following will need to be added: [.programlisting] .... vlans_em0="5" ifconfig_em0_5="inet 192.168.20.20/24" .... Additional VLANs may be added, by simply adding the tag to the `vlans_em0` field and adding an additional line configuring the network on that VLAN tag's interface. It is useful to assign a symbolic name to an interface so that when the associated hardware is changed, only a few configuration variables need to be updated. For example, security cameras need to be run over VLAN 1 on `em0`. Later, if the `em0` card is replaced with a card that uses the man:ixgb[4] driver, all references to `em0.1` will not have to change to `ixgb0.1`. To configure VLAN `5`, on the NIC `em0`, assign the interface name `cameras`, and assign the interface an IP address of `_192.168.20.20_` with a `24`-bit prefix, use this command: [source,shell] .... # ifconfig em0.5 create vlan 5 vlandev em0 name cameras inet 192.168.20.20/24 .... For an interface named `video`, use the following: [source,shell] .... # ifconfig video.5 create vlan 5 vlandev video name cameras inet 192.168.20.20/24 .... To apply the changes at boot time, add the following lines to [.filename]#/etc/rc.conf#: [.programlisting] .... vlans_video="cameras" create_args_cameras="vlan 5" ifconfig_cameras="inet 192.168.20.20/24" .... diff --git a/documentation/content/ru/books/dev-model/_index.adoc b/documentation/content/ru/books/dev-model/_index.adoc index 280822e773..15c8f1669d 100644 --- a/documentation/content/ru/books/dev-model/_index.adoc +++ b/documentation/content/ru/books/dev-model/_index.adoc @@ -1,912 +1,912 @@ --- authors: - - + - author: 'Niklas Saers' bookOrder: 45 copyright: '2002-2005 Niklas Saers' description: 'Формальное исследование организации проекта FreeBSD' layout: single tags: ["model", "project model", "FreeBSD"] title: 'Проектная модель для проекта FreeBSD' trademarks: ["freebsd", "ibm", "ieee", "adobe", "intel", "linux", "microsoft", "opengroup", "sun", "netbsd", "general"] --- //// Copyright (c) 2002-2005 Niklas Saers All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //// = Проектная модель для проекта FreeBSD :doctype: book :toc: macro :toclevels: 2 :icons: font :sectnums: :partnums: :source-highlighter: rouge :experimental: :images-path: books/dev-model/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../images/{images-path} include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] include::../../../../../shared/asciidoctor.adoc[] endif::[] ''' toc::[] [[foreword]] [.abstract-title] Предисловие -До настоящего момента проект FreeBSD выпустил ряд описанных методик для выполнения различных частей работы. Однако, из-за растущего числа участников проекта, необходима модель проекта, обобщающая его структуру. footnote:[Это согласуется с законом Брукса, согласно которому добавление нового человека в задерживающийся проект сделает его ещё более задержанным, поскольку увеличит потребность в коммуникации. Модель проекта — это инструмент для снижения потребности в коммуникации.] Данная статья предоставляет такую модель проекта и передаётся в проект документации FreeBSD, где она может развиваться вместе с проектом, чтобы в любой момент времени отражать способ его работы. Она основана на диссертации [crossref:dev-model[thesis, Saers,2003]]. +До настоящего момента проект FreeBSD выпустил ряд описанных методик для выполнения различных частей работы. Однако, из-за растущего числа участников проекта, необходима модель проекта, обобщающая его структуру. footnote:[Это согласуется с законом Брукса, согласно которому добавление нового человека в задерживающийся проект сделает его ещё более задержанным, поскольку увеличит потребность в коммуникации. Модель проекта — это инструмент для снижения потребности в коммуникации.] Данная статья предоставляет такую модель проекта и передаётся в проект документации FreeBSD, где она может развиваться вместе с проектом, чтобы в любой момент времени отражать способ его работы. Она основана на диссертации [ crossref:dev-model[thesis, Saers,2003]]. Я хотел бы поблагодарить следующих людей за то, что они нашли время объяснить мне непонятные моменты и проверить документ. * Andrey A. Chernov mailto:ache@freebsd.org[ache@freebsd.org] * Bruce A. Mah mailto:bmah@freebsd.org[bmah@freebsd.org] * Dag-Erling Smørgrav mailto:des@freebsd.org[des@freebsd.org] * Giorgos Keramidas mailto:keramida@freebsd.org[keramida@freebsd.org] * Ingvil Hovig mailto:ingvil.hovig@skatteetaten.no[ingvil.hovig@skatteetaten.no] * Jesper Holck mailto:jeh.inf@cbs.dk[jeh.inf@cbs.dk] * John Baldwin mailto:jhb@freebsd.org[jhb@freebsd.org] * John Polstra mailto:jdp@freebsd.org[jdp@freebsd.org] * Kirk McKusick mailto:mckusick@freebsd.org[mckusick@freebsd.org] * Mark Linimon mailto:linimon@freebsd.org[linimon@freebsd.org] * Marleen Devos * Niels Jørgenssen mailto:nielsj@ruc.dk[nielsj@ruc.dk] * Nik Clayton mailto:nik@freebsd.org[nik@freebsd.org] * Poul-Henning Kamp mailto:phk@freebsd.org[phk@freebsd.org] * Simon L. Nielsen mailto:simon@freebsd.org[simon@freebsd.org] [[overview]] == Обзор -Модель проекта — это способ снижения накладных расходов на коммуникации в проекте. Как показано в [crossref:dev-model[brooks, Brooks, 1995]], увеличение числа участников проекта приводит к экспоненциальному росту коммуникаций в проекте. За последние годы FreeBSD значительно увеличил как количество активных пользователей, так и коммиттеров, что соответственно привело к росту коммуникаций. Данная модель проекта поможет снизить эти накладные расходы за счёт предоставления актуального описания проекта. +Модель проекта — это способ снижения накладных расходов на коммуникации в проекте. Как показано в [ crossref:dev-model[brooks, Brooks, 1995]], увеличение числа участников проекта приводит к экспоненциальному росту коммуникаций в проекте. За последние годы FreeBSD значительно увеличил как количество активных пользователей, так и коммиттеров, что соответственно привело к росту коммуникаций. Данная модель проекта поможет снизить эти накладные расходы за счёт предоставления актуального описания проекта. -Во время выборов в Core в 2002 году Марк Мюррей заявил: «Я против длинного свода правил, так как это удовлетворяет склонности к юриспруденции и противоречит техноцентричности, в которой проект так нуждается.» [crossref:dev-model[bsd-election2002, FreeBSD, 2002B]]. Эта модель проекта не предназначена для того, чтобы оправдывать создание ограничений для разработчиков, а служит инструментом для облегчения координации. Она призвана описывать проект, давая обзор того, как выполняются различные процессы. Это введение в то, как работает проект FreeBSD. +Во время выборов в Core в 2002 году Марк Мюррей заявил: «Я против длинного свода правил, так как это удовлетворяет склонности к юриспруденции и противоречит техноцентричности, в которой проект так нуждается.» [ crossref:dev-model[bsd-election2002, FreeBSD, 2002B]]. Эта модель проекта не предназначена для того, чтобы оправдывать создание ограничений для разработчиков, а служит инструментом для облегчения координации. Она призвана описывать проект, давая обзор того, как выполняются различные процессы. Это введение в то, как работает проект FreeBSD. -Модель проекта FreeBSD будет описана по состоянию на 1 июля 2004 года. Она основана на работе Нильса Йоргенсена [crossref:dev-model[jorgensen2001, Jørgensen, 2001]], официальных документах FreeBSD, обсуждениях в списках рассылки FreeBSD и интервью с разработчиками. +Модель проекта FreeBSD будет описана по состоянию на 1 июля 2004 года. Она основана на работе Нильса Йоргенсена [ crossref:dev-model[jorgensen2001, Jørgensen, 2001]], официальных документах FreeBSD, обсуждениях в списках рассылки FreeBSD и интервью с разработчиками. После определения используемых терминов в этом документе будет описана организационная структура (включая описания ролей и линии коммуникации), рассмотрена модель методологии, а после представления инструментов, используемых для контроля процессов, будут описаны определённые процессы. В заключение будут представлены основные подпроекты проекта FreeBSD. -[crossref:dev-model[freebsd-developer-handbook, FreeBSD, 2002A]] Разделы 1.2 и 1.3 описывают видение и архитектурные принципы проекта. Видение сформулировано как: "Создать наилучший пакет операционной системы, подобной UNIX®, с должным уважением к оригинальной идеологии программных инструментов, а также к удобству использования, производительности и стабильности." Архитектурные принципы помогают определить, находится ли проблема, которую кто-то хочет решить, в рамках проекта +[ crossref:dev-model[freebsd-developer-handbook, FreeBSD, 2002A]] Разделы 1.2 и 1.3 описывают видение и архитектурные принципы проекта. Видение сформулировано как: "Создать наилучший пакет операционной системы, подобной UNIX®, с должным уважением к оригинальной идеологии программных инструментов, а также к удобству использования, производительности и стабильности." Архитектурные принципы помогают определить, находится ли проблема, которую кто-то хочет решить, в рамках проекта [[definitions]] == Определения [[ref-activity]] === Активность -"Активность" — это элемент работы, выполняемый в ходе проекта [crossref:dev-model[ref-pmbok, PMI, 2000]]. У неё есть результат, который ведёт к достижению цели. Такой результат может быть либо входом для другой активности, либо частью поставки процесса. +"Активность" — это элемент работы, выполняемый в ходе проекта [ crossref:dev-model[ref-pmbok, PMI, 2000]]. У неё есть результат, который ведёт к достижению цели. Такой результат может быть либо входом для другой активности, либо частью поставки процесса. [[def-process]] === Процесс Процесс — это ряд действий, ведущих к определённому результату. Процесс может состоять из одного или нескольких подпроцессов. Примером процесса является проектирование программного обеспечения. [[ref-hat]] === Роль (hat) "Hat" (шляпа) является синонимом роли. Роль имеет определённые обязанности в процессе и ответственность за результат процесса. Роль выполняет действия. Четко определено, по каким вопросам участники проекта и люди вне проекта должны обращаться к ответственному, выполняющему эту роль. [[ref-outcome]] === Результат -«Результат» — это конечный продукт процесса. Это синоним понятия «поставляемый результат», который определяется как «любой измеримый, осязаемый, проверяемый результат, итог или элемент, который должен быть произведён для завершения проекта или его части. Часто используется в более узком смысле в отношении внешнего поставляемого результата, который подлежит утверждению спонсором проекта или заказчиком» согласно [crossref:dev-model[ref-pmbok, PMI, 2000]]. Примерами результатов являются программное обеспечение, принятое решение или написанный отчёт. +«Результат» — это конечный продукт процесса. Это синоним понятия «поставляемый результат», который определяется как «любой измеримый, осязаемый, проверяемый результат, итог или элемент, который должен быть произведён для завершения проекта или его части. Часто используется в более узком смысле в отношении внешнего поставляемого результата, который подлежит утверждению спонсором проекта или заказчиком» согласно [ crossref:dev-model[ref-pmbok, PMI, 2000]]. Примерами результатов являются программное обеспечение, принятое решение или написанный отчёт. [[ref-freebsd]] === FreeBSD Говоря "FreeBSD", мы подразумеваем UNIX-подобную операционную систему FreeBSD, основанную на BSD, тогда как говоря "Проект FreeBSD", мы подразумеваем организацию проекта. [[model-orgstruct]] == Организационная структура Хотя никто не является владельцем FreeBSD, организация FreeBSD разделена на ядро, коммиттеров и участников и является частью сообщества FreeBSD, которое существует вокруг неё. Структура проекта FreeBSD (в порядке убывания полномочий) [.informaltable] [cols="1,1", options="header"] |=== | Группа | Количество людей |Основные участники |9 |Коммиттеры |318 |Участники |~3000 |=== Количество коммиттеров было определено путем анализа журналов CVS с 1 января 2004 года по 31 декабря 2004 года, а список участников — путем просмотра перечня внесенных изменений и отчётов о проблемах. Основной ресурс сообщества FreeBSD — это его разработчики: коммиттеры и контрибьюторы. Именно их вклад позволяет проекту развиваться. Обычные разработчики называются участниками (контрибьюторами). По состоянию на 1 января 2003 года в проекте насчитывается около 5500 контрибьюторов. Коммиттеры — это разработчики, обладающие привилегией вносить изменения. Обычно это наиболее активные разработчики, которые готовы тратить своё время не только на интеграцию собственного кода, но и на интеграцию кода, предоставленного разработчиками без такой привилегии. Они также выбирают основную команду и имеют доступ к закрытым обсуждениям. Проект можно разделить на четыре отдельные части, и большинство разработчиков сосредоточат своё участие на одной из частей FreeBSD. Эти четыре части — разработка ядра, разработка пользовательского пространства, порты и документация. Под базовой системой подразумеваются как ядро, так и пользовательское пространство. Это разделение изменяет нашу таблицу следующим образом: Структура проекта FreeBSD с участниками, имеющими права на запись, по категориям [.informaltable] [cols="1,1,1", options="header"] |=== | Группа | Категория | Количество людей |Основные участники -| +| |9 |Коммиттеры |Базовый |164 -| +| |Docs |45 -| +| |Порты |166 -| +| |Total |374 |Участники -| +| |~3000 |=== Количество коммиттеров по областям было определено путем анализа журналов CVS с 1 января 2004 года по 31 декабря 2004 года. Обратите внимание, что многие коммиттеры работают в нескольких областях, поэтому общее число больше реального количества коммиттеров. Общее количество активных уникальных коммиттеров на июнь 2022 года составляло 317. Коммиттеры делятся на три группы: коммиттеры, занимающиеся только одной областью проекта (например, файловыми системами), коммиттеры, участвующие только в одном подпроекте, и коммиттеры, вносящие изменения в разные части кода, включая подпроекты. Поскольку некоторые коммиттеры работают над разными частями, общее количество в разделе коммиттеров таблицы выше, чем в предыдущей таблице. Ядро является основным строительным блоком FreeBSD. Хотя пользовательские приложения защищены от сбоев в других пользовательских приложениях, вся система уязвима от ошибок в ядре. Это, в сочетании с огромным количеством зависимостей в ядре и тем, что нелегко увидеть все последствия изменения ядра, требует от разработчиков относительно полного понимания ядра. Множественные усилия по разработке в ядре также требуют более тесной координации, чем пользовательские приложения. Основные утилиты, известные как пользовательское окружение (userland), предоставляют интерфейс, который определяет FreeBSD, включая пользовательский интерфейс, общие библиотеки и внешние интерфейсы для подключения клиентов. В настоящее время 162 человека участвуют в разработке и поддержке пользовательского окружения, многие из которых являются сопровождающими (maintainers) для своей части кода. Вопросы сопровождения будут рассмотрены в разделе crossref:dev-model[role-maintainer,Сопровождение]. Документация обрабатывается crossref:dev-model[sub-project-documentation, Проектом документации FreeBSD] и включает все документы, связанные с проектом FreeBSD, включая веб-страницы. В течение 2004 года 101 человек внесли изменения в Проект документации FreeBSD. Порты — это коллекция метаданных, необходимых для корректной сборки программных пакетов в FreeBSD. Например, порт для веб-браузера Mozilla содержит информацию о том, откуда загружать исходный код, какие патчи применять и как, а также как пакет должен быть установлен в системе. Это позволяет автоматизированным инструментам загружать, собирать и устанавливать пакеты. На момент написания доступно более 12600 портов footnote:[Статистика получена подсчётом количества записей в файле, загруженном portsdb на 1 апреля 2005 года. portsdb является частью порта sysutils/portupgrade.], начиная от веб-серверов и игр до языков программирования и большинства типов приложений, используемых на современных компьютерах. Порты подробно рассматриваются в разделе crossref:dev-model[sub-project-ports, Подпроект Ports]. [[methodology-model]] == Методологическая модель [[development-model]] === Модель разработки Не существует определённой модели того, как люди пишут код в FreeBSD. Однако Нильс Йоргенссен предложил модель того, как написанный код интегрируется в проект. Модель Йоргенссена для интеграции изменений [.informaltable] [cols="1,1,1", options="header"] |=== | Этап | Следующий, если успешно | Следующий, если неудачно |программирование |рецензирование -| +| |рецензирование |предварительная проверка перед коммитом |программирование |предварительная проверка перед коммитом |релиз для разработки |программирование |релиз для разработки |параллельная отладка |программирование |параллельная отладка |релиз для производства |программирование |релиз для производства -| +| |программирование |=== -"Релиз для разработки" — это ветка FreeBSD-CURRENT ("-CURRENT"), а "релиз для производства" — ветка FreeBSD-STABLE ("-STABLE") [crossref:dev-model[jorgensen2001, Jørgensen, 2001]]. +"Релиз для разработки" — это ветка FreeBSD-CURRENT ("-CURRENT"), а "релиз для производства" — ветка FreeBSD-STABLE ("-STABLE") [ crossref:dev-model[jorgensen2001, Jørgensen, 2001]]. Это модель для одного изменения, которая показывает, что после написания кода разработчики ищут рецензирование сообщества и пытаются интегрировать это изменение в свои собственные системы. После интеграции изменения в версию разработки, называемую FreeBSD-CURRENT, оно тестируется многими пользователями и разработчиками сообщества FreeBSD. После достаточного тестирования оно объединяется с производственной версией, называемой FreeBSD-STABLE. Если каждая стадия не завершена успешно, разработчику необходимо вернуться, внести изменения в код и перезапустить процесс. Интеграция изменения в -CURRENT или -STABLE называется выполнением коммита. Йоргенсен обнаружил, что большинство разработчиков FreeBSD работают индивидуально, то есть эта модель используется параллельно многими разработчиками в различных текущих процессах разработки. Разработчик также может работать над несколькими изменениями одновременно, поэтому, ожидая рецензирования или тестирования одного или нескольких своих изменений, он может писать другое изменение. Поскольку каждый коммит представляет собой инкрементальное изменение, это модель с очень высокой степенью инкрементальности. Коммиты происходят настолько часто, что за один год footnote:[Для получения этого числа был исследован период с 1 января 2004 года по 31 декабря 2004 года.], было сделано 85427 коммитов, что составляет в среднем 233 коммита в день. В рамках "программирования" в модели Йоргенсена, каждый программист имеет свой собственный стиль работы и следует своим собственным моделям разработки. Этот этап вполне мог бы называться "разработкой", так как он включает сбор и анализ требований, системное и детальное проектирование, реализацию и проверку. Однако, единственным результатом этих подэтапов являются исходный код или документация системы. С точки зрения пошаговой модели (такой как каскадная модель), остальные этапы можно рассматривать как дальнейшую проверку и интеграцию системы. Эта интеграция системы также важна для определения того, будет ли изменение принято сообществом. До момента фиксации кода разработчик волен выбирать, насколько активно он будет обсуждать его с остальными участниками проекта. Чтобы -CURRENT мог выполнять роль буфера (позволяя откатывать идеи, которые оказались с невыявленными недостатками), минимальный срок, в течение которого изменения должны оставаться в -CURRENT перед слиянием в -STABLE, составляет 3 дня. Такое слияние называется MFC (Merge From Current). Важно обратить внимание на слово "изменение". Большинство коммитов не содержат радикально новых функций, а представляют собой обновления для поддержки. Единственными исключениями из этой модели являются исправления безопасности и изменения в функциях, объявленных устаревшими в ветке -CURRENT. В этих случаях изменения могут быть внесены напрямую в ветку -STABLE. В дополнение к множеству людей, работающих над проектом, существует множество связанных проектов в рамках FreeBSD. Это могут быть проекты, разрабатывающие совершенно новые функции, подпроекты или проекты, результаты которых интегрируются в FreeBSD footnote:[Например, разработка стека Bluetooth начиналась как подпроект, пока не была признана достаточно стабильной для включения в ветку -CURRENT. Теперь это часть основной системы FreeBSD.]. Эти проекты вписываются в FreeBSD так же, как и обычные разработки: они создают код, который интегрируется с проектом FreeBSD. Однако некоторые из них (например, Ports и Documentation) имеют привилегию применяться к обеим веткам или коммитить напрямую как в -CURRENT, так и в -STABLE. Не существует стандартов по выполнению проектирования, также как и централизованного репозитория для хранения проектов. Основной дизайн взят из 4.4BSD. footnote:[По словам Кирка МакКузика, после 20 лет разработки операционных систем UNIX, интерфейсы в основном уже определены. Поэтому нет необходимости в большом количестве проектирования. Однако новые применения системы и новое оборудование приводят к тому, что некоторые реализации становятся более выгодными по сравнению с ранее предпочитаемыми. Одним из примеров является появление веб-браузинга, который превратил обычное TCP/IP-соединение в короткие всплески данных, а не в устойчивый поток за более длительный период времени.] Поскольку проектирование является частью этапа "Программирование" в модели Йоргенсена, каждый разработчик или подпроект сам решает, как это должно выполняться. Даже если проектирование должно храниться в централизованном репозитории, результаты этапов проектирования будут иметь ограниченную полезность, так как различия в методологиях сделают их плохо совместимыми, если вообще совместимыми. Для общего проектирования проекта, проект полагается на подпроекты, которые договариваются о совместимых интерфейсах между собой, а не на диктат интерфейсов. [[release-branches]] === Ветви релизов Версии FreeBSD лучше всего иллюстрируются деревом с множеством ветвей, где каждая основная ветвь представляет основную версию. Минорные версии представлены ветвями основных ветвей. В следующем дереве релизов стрелки, следующие друг за другом в определённом направлении, представляют ветку. Прямоугольники со сплошными линиями и ромбы обозначают официальные релизы. Прямоугольники с пунктирными линиями представляют ветку разработки на тот момент. Ветки безопасности обозначены овалами. Ромбы отличаются от прямоугольников тем, что они представляют развилку, то есть место, где ветка разделяется на две ветки, одна из которых становится подветкой. Например, на 4.0-RELEASE ветка 4.0-CURRENT разделилась на 4-STABLE и 5.0-CURRENT. На 4.5-RELEASE ветка разветвилась на ветку безопасности под названием RELENG_4_5. .Дерево релизов FreeBSD image::branches.png["Обратитесь к таблице ниже для удобной версии для экранных дикторов."] [.informaltable] [cols="1,1,1", options="header"] |=== | Основные выпуски | Форк сделан из | Следующие минорные выпуски |... -| -| +| +| |3.0 Current (ветка разработки) -| +| |Ветки Releng 3: выпуски с 3.0 Release по 3.5 Release, ведущие к выпуску 3.5.1 Release и последующей ветке 3 Stable |4.0 Current (ветка разработки) |Релиз 3.1 |Ветви Releng 4: релизы с 4.1 по 4.6 (и релиз 4.6.2), затем релизы с 4.7 по 4.11 (все, начиная с релиза 4.3, также ведущие к ветви Releng_4_n), и последующая ветвь релиза 4 |5.0 Current (ветка разработки) |Релиз 4.0 |Ветви Releng 5: Релизы с 5.0 до 5.4 (за исключением 5.0 и 5.3, которые также ведут к ветке Releng_5_n), и последующая ветка релиза 5 |6.0 Current (ветка разработки) |Релиз 5.3 -| +| |... -| -| +| +| |=== -Последняя версия -CURRENT всегда обозначается как -CURRENT, а последний релиз -STABLE всегда обозначается как -STABLE. На этом рисунке -STABLE относится к 4-STABLE, а -CURRENT относится к 5.0-CURRENT после 5.0-RELEASE. [crossref:dev-model[freebsd-releng, FreeBSD, 2002E]] +Последняя версия -CURRENT всегда обозначается как -CURRENT, а последний релиз -STABLE всегда обозначается как -STABLE. На этом рисунке -STABLE относится к 4-STABLE, а -CURRENT относится к 5.0-CURRENT после 5.0-RELEASE. [ crossref:dev-model[freebsd-releng, FreeBSD, 2002E]] «Основной выпуск» всегда создаётся из ветки -CURRENT. Однако ветка -CURRENT не обязательно должна разветвляться в этот момент, а может сосредоточиться на стабилизации. Примером этого является то, что после 3.0-RELEASE, 3.1-RELEASE также был продолжением ветки -CURRENT, и -CURRENT не стал настоящей веткой разработки до тех пор, пока не был выпущен этот релиз и не была создана ветка 3-STABLE. Когда -CURRENT снова становится веткой разработки, за ним может следовать только основной выпуск. Ожидается, что ветка 5-STABLE будет отделена от 5.0-CURRENT примерно на момент выпуска 5.3-RELEASE. Только после отделения 5-STABLE ветка разработки получит название 6.0-CURRENT. "Минорный релиз" создаётся из ветки -CURRENT после основного релиза или из ветки -STABLE. Начиная с версии 4.3-RELEASE footnote:[Первым релизом, для которого это действительно произошло, был 4.5-RELEASE, но ветки безопасности были созданы одновременно для 4.3-RELEASE и 4.4-RELEASE.], когда выпускается минорный релиз, он становится «веткой безопасности». Это предназначено для организаций, которые не хотят следовать ветке -STABLE и потенциальным новым/изменённым функциям, которые она предлагает, но вместо этого требуют абсолютно стабильной среды, обновляемой только для внедрения исправлений безопасности. footnote:[Здесь вы видите терминологическое пересечение со словом «стабильный», что приводит к некоторой путанице. Ветка -STABLE по-прежнему является веткой разработки, цель которой — быть полезной для большинства пользователей. Если для системы неприемлемо получать изменения, которые не были объявлены на момент её развёртывания, такая система должна работать на ветке безопасности.] Каждое обновление в ветке безопасности называется "уровнем исправления" (patchlevel). Для каждого выполненного улучшения безопасности номер уровня исправления увеличивается, что позволяет легко отслеживать, какие улучшения безопасности были реализованы. В случаях особенно серьезных уязвимостей безопасности может быть выпущен полностью новый релиз из ветки безопасности. Примером этого является 4.6.2-RELEASE. [[model-summary]] === Сводка модели Для подведения итогов, модель разработки FreeBSD можно представить в виде следующего дерева: .Общая модель разработки image::freebsd-code-model.png["Обратитесь к параграфам ниже для версии, удобной для экранных дикторов."] Дерево разработки FreeBSD с текущими усилиями по разработке и непрерывной интеграцией. Дерево символизирует версии выпусков, где основные версии порождают новые главные ветви, а второстепенные версии являются версиями главной ветви. Верхняя ветвь — это ветвь -CURRENT, в которую интегрируется вся новая разработка, а ветвь -STABLE находится непосредственно под ней. Под ветвью -STABLE находятся старые, неподдерживаемые версии. Проект находится в тумане постоянной разработки, и разработчики выбирают модели разработки, которые считают подходящими. Результаты их работы затем интегрируются в -CURRENT, где проходят параллельную отладку, и наконец объединяются из -CURRENT в -STABLE. Исправления безопасности объединяются из -STABLE в ветки безопасности. Многие коммиттеры имеют специальную область ответственности. Эти роли называются "hats" (шляпами). Эти роли могут быть либо проектными ролями, например, офицер по связям с общественностью, либо сопровождающим определённой части кода. Поскольку это проект, где люди добровольно уделяют своё свободное время, люди с назначенными ролями не всегда доступны. Поэтому они должны назначить заместителя, который может выполнять эту роль в их отсутствие. Другой вариант — передать роль группе. Многие из этих ролей не формализованы. Формализованные роли имеют устав, в котором указаны точные цели, привилегии и обязанности. Написание таких уставов — новая часть проекта, поэтому оно ещё не завершено для всех ролей. Эти описания ролей не являются формализацией, а скорее представляют собой краткое описание роли со ссылками на устав, где он доступен, и контактными адресами. [[sect-hats]] == Ответственные [[general-hats]] === Стандартные роли [[role-contributor]] ==== Участник (контрибьютор) -Участник вносит вклад в проект FreeBSD в качестве разработчика, автора, отправляя отчёты о проблемах или другими способами способствуя прогрессу проекта. Участник не имеет особых привилегий в проекте FreeBSD. [crossref:dev-model[freebsd-contributors, FreeBSD, 2002F]] +Участник вносит вклад в проект FreeBSD в качестве разработчика, автора, отправляя отчёты о проблемах или другими способами способствуя прогрессу проекта. Участник не имеет особых привилегий в проекте FreeBSD. [ crossref:dev-model[freebsd-contributors, FreeBSD, 2002F]] [[role-committer]] ==== Коммиттер -Человек, обладающий необходимыми привилегиями для добавления своего кода или документации в репозиторий. Коммиттер совершил коммит в течение последних 12 месяцев. [crossref:dev-model[freebsd-developer-handbook, FreeBSD, 2000A]] Активный коммиттер — это коммиттер, который в среднем совершал один коммит в месяц в течение этого времени. +Человек, обладающий необходимыми привилегиями для добавления своего кода или документации в репозиторий. Коммиттер совершил коммит в течение последних 12 месяцев. [ crossref:dev-model[freebsd-developer-handbook, FreeBSD, 2000A]] Активный коммиттер — это коммиттер, который в среднем совершал один коммит в месяц в течение этого времени. Стоит отметить, что нет технических препятствий, которые могли бы помешать кому-либо, получившему права на коммиты в основном или подпроекте, делать коммиты в частях исходного кода проекта, для которых у коммиттера нет явного разрешения на изменение. Однако, при желании внести изменения в части, с которыми коммиттер ранее не работал, следует изучить логи, чтобы понять, что происходило в этой области ранее, а также прочитать файл MAINTAINERS, чтобы узнать, есть ли у сопровождающего этой части какие-либо особые требования к внесению изменений в код. [[role-core]] ==== Основная команда (Core Team) Основная команда избирается коммиттерами из числа коммиттеров и выполняет функции совета директоров проекта FreeBSD. Она повышает активных участников до коммиттеров, назначает людей на четко определённые роли (hats) и является окончательным арбитром при принятии решений о направлении развития проекта. На 1 июля 2004 года в состав основной команды входило 9 членов. Выборы проводятся каждые два года. [[role-maintainer]] ==== Сопровождение Сопровождение означает, что человек ответственен за то, что допускается в определённую часть кода, и имеет решающее слово в случае разногласий по поводу кода. Это включает в себя как активную работу, направленную на стимулирование участников (контрибьюторов), так и реактивную работу по рецензированию коммитов. В исходном коде FreeBSD есть файл MAINTAINERS, содержащий краткое описание того, как каждый сопровождающий предпочитает получать вклады. Наличие этого уведомления и контактной информации позволяет разработчикам сосредоточиться на разработке, а не застревать в медленной переписке, если сопровождающий будет недоступен в течение некоторого времени. Если сопровождающий недоступен в течение неоправданно долгого времени, и другие люди выполняют значительный объём работы, сопровождение может быть передано без согласия сопровождающего. Это основано на позиции, что сопровождение должно быть продемонстрировано, а не заявлено. Сопровождение определённого участка кода — это роль, которая не осуществляется коллективно. [[official-hats]] === Официальные роли Официальные роли в проекте FreeBSD — это более или менее формализованные и в основном административные должности. Они обладают полномочиями и ответственностью в своей области. В следующем списке показаны направления ответственности и дано описание каждой роли, включая информацию о том, кто её занимает. [[role-doc-manager]] ==== Менеджер проекта документации Архитектор crossref:dev-model[sub-project-documentation, Проекта документации FreeBSD] отвечает за определение и контроль целей документации для коммиттеров в проекте Документации, за которым они присматривают. Роль поддерживается: Командой DocEng mailto:doceng@FreeBSD.org[doceng@FreeBSD.org]. https://www.freebsd.org/internal/doceng/[Устав DocEng]. [[role-postmaster]] ==== Postmaster Postmaster отвечает за корректную доставку почты на адреса электронной почты коммиттеров. Также он отвечает за работоспособность почтовых рассылок и должен принимать меры против возможных сбоев в работе почты, таких как троллинг-, спам- и вирус-фильтры. Текущий руководитель: Команда почтовых серверов mailto:postmaster@FreeBSD.org[postmaster@FreeBSD.org]. [[role-release-coordination]] ==== Координация выпусков Обязанности команды выпуска релизов включают * Установка, публикация и соблюдение графика выпуска официальных релизов * Документирование и формализация процедур выпуска релизов * Создание и поддержка веток кода * Согласование с командами Ports и Documentation для выпуска обновленного набора пакетов и документации вместе с новыми релизами * Координация с командой безопасности для того, чтобы готовящиеся выпуски не были затронуты недавно обнаруженными уязвимостями. Дополнительная информация о процессе разработки доступна в разделе crossref:dev-model[process-release-engineering, Выпуск релизов]. [[role-releng]] Роль поддерживается: командой выпуск релизов (Release Engineering) mailto:re@FreeBSD.org[re@FreeBSD.org]. https://www.freebsd.org/releng/charter/[ Устав Release Engineering]. [[role-pr-cr]] ==== Отношения с общественностью и корпоративные связи Обязанности отдела по связям с общественностью и корпоративным отношениям включают: * Публиковать пресс-релизы при возникновении событий, важных для проекта FreeBSD. * Быть официальным контактным лицом для корпораций, тесно сотрудничающих с проектом FreeBSD. * Принимать меры для продвижения FreeBSD как в сообществе Open Source, так и в корпоративном мире. * Обрабатывать список рассылки "freebsd-advocacy". Эта роль в настоящее время не занята. [[role-security-officer]] ==== Ответственный за безопасность (Security Officer) Основная обязанность Ответственного за безопасность — координировать обмен информацией с сообществом безопасности и проектом FreeBSD. Ответственный за безопасность также принимает меры при поступлении сообщений о проблемах безопасности и способствует активному развитию в области безопасности. Из-за опасений, что информация об уязвимостях может попасть к злоумышленникам до выпуска исправления, только Ответственный за безопасность, включающий руководителя, заместителя и двух членов crossref:dev-model[role-core, Основной команды (Core Team)], получает конфиденциальную информацию о проблемах безопасности. Однако для создания или внедрения исправления Ответственный за безопасность может обратиться к команде mailto:security-team@FreeBSD.org[security-team@FreeBSD.org] для помощи в выполнении работы. [[role-repo-manager]] ==== Менеджер репозитория исходного кода Менеджер репозитория исходного кода — единственный, кому разрешено напрямую изменять репозиторий без использования инструмента crossref:dev-model[tool-git, Git]. В его обязанности входит оперативное решение технических проблем, возникающих в репозитории. Менеджер репозитория исходного кода имеет право отменять коммиты, если это необходимо для устранения технических проблем с Git. Роль принадлежит: Менеджеру репозитория исходного кода mailto:clusteradm@FreeBSD.org[clusteradm@FreeBSD.org]. [[role-election-manager]] ==== Менеджер выборов Менеджер выборов отвечает за процесс crossref:dev-model[process-core-election,выборов Основной команды(Core Team)]. Он отвечает за проведение и поддержание системы выборов, а также является окончательной инстанцией в случае незначительных непредвиденных событий в процессе выборов. Крупные непредвиденные события должны обсуждаться с crossref:dev-model[role-core,Основной командой] Роль выполняется только во время выборов. [[role-webmaster]] ==== Управление веб-сайтом Роль управления веб-сайтом отвечает за координацию развертывания обновленных веб-страниц на зеркалах по всему миру, за общую структуру основного веб-сайта и систему, на которой он работает. Управление должно согласовывать содержимое с crossref:dev-model[sub-project-documentation, Проектом документации FreeBSD] и выступает в роли сопровождающего для дерева "www". Роль поддерживается: веб-мастеры FreeBSD mailto:www@FreeBSD.org[www@FreeBSD.org]. [[role-ports-manager]] ==== Менеджер портов Менеджер портов выступает в роли связующего звена между crossref:dev-model[sub-project-ports, Подпроектом портов] и основным проектом, и все запросы от проекта должны направляться менеджеру портов. Роль принадлежит: Команде управления портами mailto:portmgr@FreeBSD.org[portmgr@FreeBSD.org]. https://www.freebsd.org/portmgr/charter/[Устав Portmgr]. [[role-standards]] ==== Стандарты Роль стандартов отвечает за обеспечение соответствия FreeBSD стандартам, которым система следует, отслеживает развитие этих стандартов и уведомляет разработчиков FreeBSD о важных изменениях. Это позволяет разработчикам действовать проактивно и сокращать время между обновлением стандартов и достижением соответствия в FreeBSD. Текущий ответственный: Garrett Wollman mailto:wollman@FreeBSD.org[wollman@FreeBSD.org]. [[role-core-secretary]] ==== Секретарь Основной команды (Core Team) Основная обязанность Секретаря Основной команды — составление черновиков и публикация окончательных отчётов Основной команды. Секретарь также ведёт повестку Основной команды, гарантируя, что ни один вопрос не останется без решения. Ответственный в настоящее время: {rene}. [[role-bugmeister]] ==== Ответственный за ошибки (Bugmeister) Ответственный за ошибки (Bugmeister) отвечает за поддержание базы данных по обслуживанию в рабочем состоянии, за корректную категоризацию записей и отсутствие недействительных записей. Они курируют исправителей ошибок (bugbusters). Текущий ответственный: команда Bugmeister Team mailto:bugmeister@FreeBSD.org[bugmeister@FreeBSD.org]. [[role-donations]] ==== Представитель по привлечению пожертвований Задача представителя по привлечению пожертвований — связывать разработчиков, которым что-то нужно, с людьми или организациями, готовыми сделать пожертвование. Роль поддерживается: Отделом по привлечению пожертвований mailto:donations@FreeBSD.org[donations@FreeBSD.org]. https://www.freebsd.org/donations/[ Устав Отдела по привлечению пожертвований]. [[role-admin]] ==== Администратор (Admin) (Также называется "Администратор кластера FreeBSD") Команда администраторов состоит из людей, ответственных за администрирование компьютеров, которые проект использует для распределённой работы и синхронизации коммуникации. В основном в неё входят те, кто имеет физический доступ к серверам. Роль поддерживается: командой администраторов mailto:admin@FreeBSD.org[admin@FreeBSD.org]. [[proc-depend-hats]] === Процессозависимые роли [[role-problem-originator]] ==== Инициатор отчёта Лицо, изначально ответственное за подачу отчёта о проблеме. [[role-bugbuster]] ==== Исправитель ошибок (Bugbuster) Человек, который либо найдет подходящего специалиста для решения проблемы, либо закроет PR, если он является дубликатом или по другим причинам не представляет интереса. [[role-mentor]] ==== Наставник (Mentor) Наставник — это коммиттер, который берет на себя задачу ознакомить нового коммиттера с проектом. Это включает в себя проверку корректности настройки окружения нового коммиттера, обучение доступным инструментам, необходимым для работы, а также разъяснение ожидаемого поведения. [[role-vendor]] ==== Поставщик Лицо (лица) или организация, от которых поступает внешний код и которым отправляются исправления. [[role-reviewer]] ==== Рецензенты Люди из списка рассылки, куда отправлен запрос на рецензирование. Следующий раздел описывает установленные процессы проекта. Вопросы, не охваченные этими процессами, решаются по мере возникновения, исходя из сложившейся практики в аналогичных случаях. [[model-processes]] == Процессы [[proc-addrem-committer]] === Добавление новых и удаление старых коммиттеров Основная команда (Core Team) отвечает за предоставление и отзыв прав на коммит для участников. Это может быть сделано только через голосование в списке рассылки Основной команды. Подпроекты ports и documentation могут предоставлять права на коммит людям, работающим над этими проектами, но на данный момент не отзывали такие права. Обычно кандидата в коммиттеры основной команде (Core Team) рекомендуют коммиттеры. Для участников или посторонних обращаться в Основную команду с просьбой стать коммиттером считается неблагоразумным и такая просьба, как правило, отклоняется. Если область, представляющая особый интерес для разработчика, потенциально пересекается с зоной ответственности других сопровождающих, запрашивается мнение этих сопровождающих. Однако часто именно этот сопровождающий рекомендует разработчика. Когда участнику предоставляется статус коммиттера, ему назначается наставник. В общем случае коммиттер, который рекомендовал нового коммиттера, берет на себя обязанности наставника для нового коммиттера. Когда участнику предоставляется право коммита (commit bit), отправляется подписанное с помощью crossref:dev-model[tool-pgp, Pretty Good Privacy] письмо от crossref:dev-model[role-core-secretary, Секретаря Основной команды], crossref:dev-model[role-ports-manager, Менеджера портов] или nik@freebsd.org на адреса admins@freebsd.org, назначенного наставника, нового коммиттера и Основной команды, подтверждая одобрение новой учётной записи. Затем наставник собирает строку пароля, crossref:dev-model[tool-ssh2, Secure Shell] открытый ключ и PGP-ключ от нового коммиттера и отправляет их crossref:dev-model[role-admin, Администратору]. Когда новая учётная запись создана, наставник активирует право коммита и проводит нового коммиттера через остальные этапы начального процесса. .Процесс вкратце: добавление нового коммиттера image::proc-add-committer.png["Обратитесь к абзацу ниже для версии, совместимой с программами чтения с экрана."] Когда участник отправляет фрагмент кода, принимающий коммиттер может предложить предоставить этому участнику права на коммит. Если он рекомендует это основной команде (Core Team), команда проводит голосование по этой рекомендации. Если голосование завершается в пользу предложения, новому коммиттеру назначается наставник, и новый коммиттер должен отправить свои данные администраторам для создания учётной записи. После этого новый коммиттер готов сделать свой первый коммит. По традиции, это делается путём добавления своего имени в список коммиттеров. Напомним, что коммиттером считается тот, кто за последние 12 месяцев внёс изменения в код. Однако право на коммиты может быть отозвано только после 18 месяцев неактивности. -[crossref:dev-model[freebsd-expiration-policy, FreeBSD, 2002H]] +[ crossref:dev-model[freebsd-expiration-policy, FreeBSD, 2002H]] Однако не существует автоматических процедур для этого. Для действий, связанных с привилегиями коммитов, не вызванных временем, см. crossref:dev-model[process-reactions,раздел 1.5.8]. .Процесс: удаление коммиттера image::proc-rm-committer.png["Обратитесь к абзацу ниже для версии, совместимой с программами чтения с экрана."] Когда Основная команда (Core Team) принимает решение очистить список коммиттеров, они проверяют, кто не делал коммитов за последние 18 месяцев. Коммиттеры, которые этого не сделали, лишаются прав на коммит, и их учётные записи удаляются администраторами. Также возможно для коммиттеров запросить отзыв их права на коммит, если по какой-то причине они больше не будут активно участвовать в проекте. В этом случае, право может быть восстановлено позже по запросу коммиттера. Роли в этом процессе: . crossref:dev-model[role-core, Основная команда (Core Team)] . crossref:dev-model[role-contributor, Участник (контрибьютор)] . crossref:dev-model[role-committer, Коммиттер] . crossref:dev-model[role-maintainer, Сопровождение] . crossref:dev-model[role-mentor, Наставник (Mentor)] -[crossref:dev-model[freebsd-bylaws, FreeBSD, 2000A]] -[crossref:dev-model[freebsd-expiration-policy, FreeBSD, 2002H]] -[crossref:dev-model[freebsd-new-account, FreeBSD, 2002I]] +[ crossref:dev-model[freebsd-bylaws, FreeBSD, 2000A]] +[ crossref:dev-model[freebsd-expiration-policy, FreeBSD, 2002H]] +[ crossref:dev-model[freebsd-new-account, FreeBSD, 2002I]] [[committing]] === Коммит кода Добавление нового или изменённого кода — один из наиболее частых процессов в проекте FreeBSD и обычно происходит несколько раз в день. Фиксация кода может быть выполнена только "коммиттером". Коммиттеры фиксируют либо код, написанный ими самими, либо код, переданный им, либо код, отправленный через crossref:dev-model[model-pr,отчёт о проблеме]. Когда разработчик пишет нетривиальный код, он должен запросить рецензирование кода у сообщества. Это делается путём отправки письма в соответствующий список рассылки с просьбой о рецензировании. Перед отправкой кода на проверку разработчик должен убедиться, что он корректно компилируется со всем деревом исходного кода и что все соответствующие тесты выполняются. Это называется "предварительной проверкой перед коммитом". Когда получен вклад в виде кода, коммиттер должен просмотреть его и протестировать таким же образом. Когда изменение фиксируется в части исходного кода, которая была получена от внешнего crossref:dev-model[role-vendor,поставщика], сопровождающий должен убедиться, что патч передан обратно поставщику. Это соответствует философии открытого исходного кода и упрощает синхронизацию с внешними проектами, так как патчи не придётся применять заново при каждом новом выпуске. После того как код был доступен для рецензирования и дальнейшие изменения не требуются, код вносится в ветку разработки -CURRENT. Если изменение применимо и для ветки -STABLE, или других веток, коммиттер устанавливает отсчёт времени для "слияния из текущей" ("MFC"). После того как пройдёт количество дней, выбранное коммиттером при установке MFC, автоматически будет отправлено письмо коммиттеру с напоминанием внести изменения в ветку -STABLE (а также, возможно, в ветки безопасности). В ветки безопасности следует сливать только критические изменения, связанные с безопасностью. Откладывание коммита в -STABLE и другие ветки позволяет проводить "параллельную отладку", когда закоммиченный код тестируется на широком спектре конфигураций. Это приводит к тому, что изменения в -STABLE содержат меньше ошибок, что и даёт ветке её название. .Процесс вкратце: коммиттер делает коммит кода image::proc-commit.png["Обратитесь к абзацу ниже для версии, совместимой с программами чтения с экрана."] Когда коммиттер написал часть кода и хочет его закоммитить, он сначала должен определить, достаточно ли он тривиален, чтобы попасть в репозиторий без предварительной рецензии, или ему сначала следует сделать рецензию в сообществе разработчиков. Если код тривиален или был отрецензирован, и коммиттер не является сопровождающим, он должен проконсультироваться с сопровождающим перед тем, как продолжить. Если код предоставлен внешним поставщиком, сопровождающий должен создать патч, который отправляется обратно поставщику. Затем код коммитится и развертывается пользователями. Если они обнаружат проблемы с кодом, это будет сообщено, и коммиттер может вернуться к написанию патча. Если затронут поставщик, он может выбрать реализацию или игнорирование патча. .Процесс вкратце: Участник делает коммит кода image::proc-contrib.png["Обратитесь к абзацам выше и ниже для версии, совместимой с программами чтения с экрана."] Разница, когда участник вносит код, заключается в том, что он отправляет код через интерфейс Bugzilla. Этот отчёт забирает сопровождающий, который просматривает код и делает ему коммит. Роли, задействованные в этом процессе: . crossref:dev-model[role-committer, Коммиттер] . crossref:dev-model[role-contributor, Участник (контрибьютор)] . crossref:dev-model[role-vendor, Поставщик] . crossref:dev-model[role-reviewer, Рецензенты] -[crossref:dev-model[freebsd-committer, FreeBSD, 2001]] -[crossref:dev-model[jorgensen2001, Jørgensen, 2001]] +[ crossref:dev-model[freebsd-committer, FreeBSD, 2001]] +[ crossref:dev-model[jorgensen2001, Jørgensen, 2001]] [[process-core-election]] === Выборы основной команды (Core Team) Выборы Основной команды проводятся не реже чем раз в два года. footnote:[Первые выборы Основной команды состоялись в сентябре 2000 года] Избираются девять участников Основной команды. Новые выборы проводятся, если количество участников Основной команды становится меньше семи. Новые выборы также могут быть проведены, если этого потребуют как минимум 1/3 активных коммиттеров. Когда должны состояться выборы, Основная команда объявляет об этом как минимум за 6 недель и назначает менеджера выборов для их проведения. Только коммиттеры могут быть избраны в состав основной команды (Core Team). Кандидаты должны подать свои заявки как минимум за одну неделю до начала выборов, но могут уточнять свои заявления до начала голосования. Они представлены в http://election.uk.freebsd.org/candidates.html[списке кандидатов]. При составлении своих предвыборных заявлений кандидаты должны ответить на несколько стандартных вопросов, предоставленных организатором выборов. Во время выборов строго соблюдается правило, что коммиттер должен был сделать коммит в течение последних 12 месяцев. Только эти коммиттеры имеют право голосовать. При голосовании коммиттер может проголосовать один раз в поддержку до девяти номинантов. Голосование проводится в течение четырёх недель, с напоминаниями, публикуемыми в рассылке "developers", доступной всем коммиттерам. Результаты выборов публикуются через неделю после их окончания, а новая основная команда вступает в должность через неделю после публикации результатов. В случае ничьей при голосовании, это будет разрешено новыми, однозначно избранными членами ядра. Голоса и заявления кандидатов архивируются, но архивы не являются общедоступными. .Процесс вкратце: Выборы основной команды (Core Team) image::proc-elections.png["Обратитесь к абзацу ниже для версии, совместимой с программами чтения с экрана."] Основная команда объявляет выборы и назначает руководителя выборов, который подготавливает процесс. Когда всё готово, кандидаты могут объявить о своей кандидатуре, представив заявления. Затем коммиттеры голосуют. После завершения голосования результаты выборов объявляются, и новая основная команда вступает в должность. Ответственный за выборы Основной команды: * crossref:dev-model[role-core, Основная команда (Core Team)] * crossref:dev-model[role-committer, Коммиттер] * crossref:dev-model[role-election-manager, Менеджер выборов] -[crossref:dev-model[freebsd-bylaws, FreeBSD, 2000A]] -[crossref:dev-model[bsd-election2002, FreeBSD, 2002B]] -[crossref:dev-model[freebsd-election, FreeBSD, 2002G]] +[ crossref:dev-model[freebsd-bylaws, FreeBSD, 2000A]] +[ crossref:dev-model[bsd-election2002, FreeBSD, 2002B]] +[ crossref:dev-model[freebsd-election, FreeBSD, 2002G]] [[new-features]] === Разработка новых функций -В рамках проекта существуют подпроекты, работающие над новыми функциями. Эти проекты обычно выполняются одним человеком [crossref:dev-model[jorgensen2001, Йоргенсен, 2001]]. Каждый проект волен организовывать разработку так, как считает нужным. Однако, когда проект объединяется с ветвью -CURRENT, он должен следовать руководствам проекта. Когда код хорошо протестирован в ветви -CURRENT и признан достаточно стабильным и актуальным для ветви -STABLE, он объединяется с ветвью -STABLE. +В рамках проекта существуют подпроекты, работающие над новыми функциями. Эти проекты обычно выполняются одним человеком [ crossref:dev-model[jorgensen2001, Йоргенсен, 2001]]. Каждый проект волен организовывать разработку так, как считает нужным. Однако, когда проект объединяется с ветвью -CURRENT, он должен следовать руководствам проекта. Когда код хорошо протестирован в ветви -CURRENT и признан достаточно стабильным и актуальным для ветви -STABLE, он объединяется с ветвью -STABLE. Требования проекта определяются пожеланиями разработчиков, запросами сообщества в виде прямых обращений по почте, отчётов о проблемах (Problem Reports), коммерческим финансированием разработки функциональности или вкладами научного сообщества. Пожелания, которые входят в зону ответственности разработчика, передаются этому разработчику, который расставляет приоритеты между запросом и своими собственными пожеланиями. Распространенный способ организации этого процесса — ведение списка задач (TODO-list), поддерживаемого проектом. Задачи, не входящие в чью-либо зону ответственности, собираются в списках TODO, пока кто-нибудь не возьмет на себя ответственность за их выполнение. Все запросы, их распределение и отслеживание обрабатываются с помощью инструмента crossref:dev-model[tool-bugzilla, Bugzilla]. Анализ требований происходит двумя способами. Поступившие запросы обсуждаются в почтовых рассылках, как в основном проекте, так и в подпроекте, к которому относится запрос или который создаётся этим запросом. Кроме того, отдельные разработчики подпроекта оценивают осуществимость запросов и определяют приоритеты между ними. Помимо архивов обсуждений, на этом этапе не создаётся никаких результатов, которые включаются в основной проект. Поскольку запросы приоритизируются отдельными разработчиками на основе того, что они считают интересным, необходимым или за что им платят, отсутствует общая стратегия или приоритезация того, какие запросы считать требованиями, и как контролировать их корректную реализацию. Однако большинство разработчиков разделяют общее видение того, какие вопросы являются более важными, и они могут запросить рекомендации у команды инженеров по выпуску релизов. Фаза проверки проекта состоит из двух этапов. Перед внесением кода в текущую ветку разработчики запрашивают рецензирование своего кода коллегами. Это рецензирование в основном проводится с помощью функционального тестирования, но также важна проверка кода. Когда код внесён в ветку, проводится более широкое функциональное тестирование, которое может привести к дополнительной проверке кода и отладке, если код ведёт себя не так, как ожидалось. Эта вторая форма проверки может рассматриваться как структурная верификация. Хотя сами подпроекты могут писать формальные тесты, такие как модульные тесты, они обычно не собираются основным проектом и чаще всего удаляются перед внесением кода в текущую ветку. footnote:[Однако всё больше тестов выполняется при сборке системы (make world). Эти тесты являются очень новым дополнением, и систематическая структура для них ещё не создана.] [[model-maintenance]] === Сопровождение Для проекта полезно, чтобы за каждую область исходного кода отвечал хотя бы один человек, который хорошо её знает. Некоторые части кода имеют назначенных сопровождающих. Другие имеют фактических сопровождающих, а некоторые части системы не имеют сопровождающих. Сопровождающий обычно является участником подпроекта, который написал и интегрировал код, или тем, кто портировал его с платформы, для которой он был написан. footnote:[sendmail и named — примеры кода, который был объединён с других платформ.] Задача сопровождающего — убедиться, что код синхронизирован с проектом, из которого он получен, если это сторонний код, а также применять патчи, предоставленные сообществом, или исправлять обнаруженные проблемы. -Основной объём работы, вкладываемый в проект FreeBSD, связан с сопровождением. [crossref:dev-model[jorgensen2001, Jørgensen, 2001]] предоставляет схему, показывающую жизненный цикл изменений. +Основной объём работы, вкладываемый в проект FreeBSD, связан с сопровождением. [ crossref:dev-model[jorgensen2001, Jørgensen, 2001]] предоставляет схему, показывающую жизненный цикл изменений. Модель Йоргенссена для интеграции изменений [.informaltable] [cols="1,1,1", options="header"] |=== | Этап | Следующий, если успешно | Следующий, если неудачно |программирование |рецензирование -| +| |рецензирование |предварительная проверка перед коммитом |программирование |предварительная проверка перед коммитом |релиз для разработки |программирование |релиз для разработки |параллельная отладка |программирование |параллельная отладка |релиз для производства |программирование |релиз для производства -| +| |программирование |=== Здесь "релиз для разработки" относится к ветке -CURRENT, а "релиз для производства" — к ветке -STABLE. "Предварительная проверка перед коммитом" — это функциональное тестирование, проводимое коллегами-разработчиками по запросу или для проверки кода с целью определения состояния подпроекта. "Параллельная отладка" — это функциональное тестирование, которое может вызвать дополнительный обзор и отладку, когда код включён в ветку -CURRENT. На момент написания этого документа в проекте было 269 коммиттеров. Когда они вносят изменения в ветку, это создаёт новый выпуск. Очень часто пользователи в сообществе отслеживают определённую ветку. Мгновенное появление нового выпуска делает изменения широко доступными сразу же и позволяет быстро получать отзывы от сообщества. Это также даёт сообществу ожидаемое время реакции на проблемы, которые важны для них. Это делает сообщество более вовлеченным, что, в свою очередь, позволяет получать больше и лучше отзывов, что снова стимулирует больше сопровождения и в конечном итоге должно создать лучший продукт. Прежде чем вносить изменения в код в частях дерева, история которых неизвестна коммиттеру, коммиттер обязан прочитать журналы коммитов, чтобы понять, почему определённые функции реализованы именно так, и избежать ошибок, которые уже были обдуманы или исправлены ранее. [[model-pr]] === Сообщение о проблеме До FreeBSD 10 в FreeBSD входил инструмент для отправки отчётов о проблемах под названием `send-pr`. Проблемы включают отчёты об ошибках, запросы функций, улучшения функций и уведомления о новых версиях внешнего программного обеспечения, включённого в проект. Хотя `send-pr` доступен, пользователям и разработчикам рекомендуется отправлять проблемы, используя нашу https://bugs.freebsd.org/submit/[форму отчёта о проблемах]. Отчёты о проблемах отправляются на электронный адрес, откуда они попадают в базу данных сопровождения отчётов о проблемах. crossref:dev-model[role-bugbuster, Исправитель ошибок (Bugbuster)] классифицирует проблему и направляет её соответствующей группе или сопровождающему в рамках проекта. После того, как кто-то берёт ответственность за отчёт, он анализируется. Этот анализ включает проверку проблемы и разработку решения. Часто требуется обратная связь от автора отчёта или даже от сообщества FreeBSD. Как только создаётся патч для устранения проблемы, автора отчёта могут попросить его протестировать. В итоге рабочий патч интегрируется в проект и, если необходимо, документируется. Затем он проходит стандартный цикл сопровождения, как описано в разделе crossref:dev-model[model-maintenance, Сопровождение]. Отчёт о проблеме может находиться в следующих состояниях: открыт, анализируется, ожидает обратной связи, исправлен патчем, отложен и закрыт. Состояние "отложен" используется, когда дальнейшее продвижение невозможно из-за недостатка информации или когда задача требует столько работы, что в данный момент никто над ней не работает. .Сводка процесса: сообщение о проблеме image::proc-pr.png["Обратитесь к абзацу ниже для версии, совместимой с программами чтения с экрана."] Проблема сообщается автором отчёта. Затем она классифицируется ответственным за обработку ошибок и передаётся соответствующему сопровождающему. Он проверяет проблему и обсуждает её с автором отчёта до тех пор, пока не будет собрано достаточно информации для создания рабочего исправления. Это исправление затем фиксируется, и отчёт о проблеме закрывается. Роли, включенные в этот процесс: . crossref:dev-model[role-problem-originator, Инициатор отчёта] . crossref:dev-model[role-maintainer, Сопровождение] . crossref:dev-model[role-bugbuster, Исправитель ошибок (Bugbuster)] -[crossref:dev-model[freebsd-handle-pr, FreeBSD, 2002C]]. -[crossref:dev-model[freebsd-send-pr, FreeBSD, 2002D]] +[ crossref:dev-model[freebsd-handle-pr, FreeBSD, 2002C]]. +[ crossref:dev-model[freebsd-send-pr, FreeBSD, 2002D]] [[process-reactions]] === Реагирование на неправильное поведение -[crossref:dev-model[freebsd-committer, FreeBSD, 2001]] содержит ряд правил, которым должны следовать коммиттеры. Однако случается, что эти правила нарушаются. Следующие правила существуют для того, чтобы можно было реагировать на неподобающее поведение. Они определяют, какие действия приведут к приостановке привилегий коммиттера на тот или иной срок. +[ crossref:dev-model[freebsd-committer, FreeBSD, 2001]] содержит ряд правил, которым должны следовать коммиттеры. Однако случается, что эти правила нарушаются. Следующие правила существуют для того, чтобы можно было реагировать на неподобающее поведение. Они определяют, какие действия приведут к приостановке привилегий коммиттера на тот или иной срок. * Совершение коммитов во время заморозки кода без одобрения команды Release Engineering — 2 дня * Коммит изменений в ветку безопасности без одобрения - 2 дня * Войны коммитов — 5 дней для всех участвующих сторон * Невежливое или неподобающее поведение — 5 дней -[crossref:dev-model[ref-freebsd-trenches, Lehey, 2002]] +[ crossref:dev-model[ref-freebsd-trenches, Lehey, 2002]] Для эффективности приостановок любой член основной команды (Core Team) может применить приостановку до обсуждения на почтовой рассылке "core". Повторные нарушители могут, при 2/3 голосов от основной команды, получить более строгие наказания, включая постоянное лишение прав на коммиты. (Однако последнее всегда рассматривается как крайняя мера из-за присущей ему склонности вызывать споры.) Все приостановки публикуются в почтовой рассылке "developers", доступной только коммиттерам. Важно, что вас не могут приостановить за технические ошибки. Все наказания связаны с нарушением социального этикета. Роли, участвующие в этом процессе: * crossref:dev-model[role-core, Основная команда (Core Team)] * crossref:dev-model[role-committer, Коммиттер] [[process-release-engineering]] === Выпуск релизов Проект FreeBSD имеет команду инженеров по выпуску релизов с главным инженером, который отвечает за создание релизов FreeBSD для распространения среди пользователей через интернет или продажи в розничных магазинах. Поскольку FreeBSD доступна на нескольких платформах, а релизы для различных архитектур выпускаются одновременно, в команде есть ответственный за каждую архитектуру. Также в команде есть роли, отвечающие за координацию усилий по обеспечению качества, сборку набора пакетов и актуализацию документации. Под инженером по выпуску релизов подразумевается представитель команды инженеров по выпуску релизов. Когда готовится выпуск релиза, проект FreeBSD несколько меняет свою структуру. Составляется график выпуска, включающий заморозку функциональности и кода, выпуск промежуточных релизов и финального релиза. Заморозка функциональности означает, что новые функции не могут быть добавлены в ветку без явного согласия инженеров релиза. Заморозка кода означает, что изменения в коде (например, исправления ошибок) не могут быть добавлены без явного согласия инженеров релиза. Этот процесс заморозки функциональности и кода известен как стабилизация. В процессе выпуска релиза инженер релиза имеет полномочия откатываться к более старым версиям кода и, таким образом, "отменять" изменения, если они сочтут, что эти изменения не подходят для включения в релиз. Существует три различных вида выпусков: . .0 выпуски являются первым релизом основной версии. Они ветвятся от ветки -CURRENT и имеют значительно более длительный цикл разработки из-за нестабильного характера ветки -CURRENT . .X релизы — это релизы ветки -STABLE. Они запланированы к выходу каждые 4 месяца. . .X.Y — это выпуски с исправлениями уязвимостей, следующие за веткой .X. Они выходят только тогда, когда с момента последнего выпуска в этой ветке было объединено достаточное количество исправлений уязвимостей. Новые функции включаются редко, а команда безопасности участвует в этих выпусках гораздо активнее, чем в обычных. Для выпусков ветки -STABLE процесс выпуска начинается за 45 дней до предполагаемой даты релиза. В течение первой фазы, первых 15 дней, разработчики переносят изменения из -CURRENT, которые они хотят включить в релиз, в ветку выпуска. По окончании этого периода код входит в 15-дневный период заморозки, в течение которого допускаются только исправления ошибок, обновления документации, исправления, связанные с безопасностью, и незначительные изменения драйверов устройств. Эти изменения должны быть предварительно одобрены инженером выпуска. В начале последнего 15-дневного периода создаётся кандидат на выпуск для широкого тестирования. В этот период вероятность внесения изменений снижается, за исключением важных исправлений ошибок и обновлений безопасности. В этот заключительный период все выпуски считаются кандидатами на выпуск. По завершении процесса выпуска создаётся релиз с новым номером версии, включая бинарные дистрибутивы на веб-сайтах и создание образов CD-ROM. Однако релиз не считается «действительно выпущенным» до тех пор, пока на список рассылки freebsd-announce не будет отправлено сообщение, подписанное с помощью crossref:dev-model[tool-pgp, Pretty Good Privacy], в котором явно указано, что релиз состоялся; все, что обозначено как «релиз» до этого момента, может находиться в процессе доработки и изменяться до отправки PGP-подписанного сообщения. footnote:[Многие коммерческие поставщики используют эти образы для создания CD-ROM, которые продаются в розничных магазинах.]. Версии ветки -CURRENT (то есть все версии, оканчивающиеся на ".0"), очень похожи, но с вдвое большим временным промежутком. Процесс начинается за 8 недель до выпуска с объявления графика релиза. Через две недели после начала процесса выпуска вводится заморозка функциональности, и оптимизация производительности должна быть сведена к минимуму. За четыре недели до выпуска становится доступна официальная бета-версия. За две недели до выпуска код официально ветвится в новую версию. Этой версии присваивается статус релиз-кандидата, и, как и в случае с разработкой -STABLE, заморозка кода релиз-кандидата ужесточается. Однако разработка на основной ветке разработки может продолжаться. За исключением этих различий, процессы разработки релизов схожи. *.0 выпуски выделяются в отдельную ветку и ориентированы в основном на ранних последователей. Затем ветка проходит период стабилизации, и только после того, как crossref:dev-model[role-releng, Команда разработки релизов] решит, что требования к стабильности выполнены, ветка становится -STABLE, а -CURRENT переключается на следующую мажорную версию. Хотя в большинстве случаев это происходило с версиями *.1, это не является обязательным требованием. Большинство выпусков происходит по достижении даты, которая считается достаточно отдалённой от предыдущего выпуска. Установлена цель выпускать основные версии каждые 18 месяцев, а промежуточные — каждые 4 месяца. Сообщество пользователей чётко дало понять, что безопасность и стабильность не могут быть принесены в жертву из-за самостоятельно установленных сроков и целевых дат выпуска. Чтобы задержки не становились слишком длинными в вопросах безопасности и стабильности, требуется дополнительная дисциплина при внесении изменений в -STABLE. . Сделать график выпуска релизов . Заморозить функциональность . Заморозка кода . Создать ветку . Кандидат на выпуск . Стабилизировать выпуск (при необходимости вернуться к предыдущему шагу; когда выпуск считается стабильным, перейти к следующему шагу) . Собрать пакеты . Предупредить сайты-зеркала . Опубликовать выпуск // Keep the spaces around the external square bracket to avoid a warning in the // PDF converter [ crossref:dev-model[freebsd-releng, FreeBSD, 2002E] ] [[tools]] == Инструменты Основные инструменты поддержки процесса разработки — это Bugzilla, Mailman и OpenSSH. Это инструменты, разработанные сторонними организациями, которые широко используются в мире открытого исходного кода. [[tool-git]] === Git Git — это система для управления несколькими версиями текстовых файлов, отслеживания внесённых изменений, их авторов и причин. Проект хранится в «репозитории», а разные версии считаются разными «ветками». [[tool-bugzilla]] === Bugzilla Bugzilla — это база данных для сопровождения, состоящая из набора инструментов для отслеживания ошибок на центральном сайте. Она поддерживает процесс отслеживания ошибок, включая отправку и обработку ошибок, а также запросы и обновление базы данных, а также редактирование отчётов об ошибках. Проект использует веб-интерфейс для отправки "Отчётов о проблемах" на центральный сервер Bugzilla проекта. У коммиттеров также есть веб- и командные клиенты. [[model-mailman]] === Mailman -Mailman - это программа, которая автоматизирует управление почтовыми рассылками. Проект FreeBSD использует её для ведения 16 общих рассылок, 60 технических рассылок, 4 ограниченных рассылок и 5 рассылок с логами коммитов Git. Она также используется для многих почтовых рассылок, созданных и используемых другими людьми и проектами в сообществе FreeBSD. Общие рассылки предназначены для широкой публики, технические рассылки в основном предназначены для разработки определённых областей интересов, а закрытые рассылки используются для внутренней коммуникации, не предназначенной для широкой публики. Большая часть всей коммуникации в проекте проходит через эти 85 рассылок [crossref:dev-model[ref-bsd-handbook, FreeBSD, 2003A], Приложение C]. +Mailman - это программа, которая автоматизирует управление почтовыми рассылками. Проект FreeBSD использует её для ведения 16 общих рассылок, 60 технических рассылок, 4 ограниченных рассылок и 5 рассылок с логами коммитов Git. Она также используется для многих почтовых рассылок, созданных и используемых другими людьми и проектами в сообществе FreeBSD. Общие рассылки предназначены для широкой публики, технические рассылки в основном предназначены для разработки определённых областей интересов, а закрытые рассылки используются для внутренней коммуникации, не предназначенной для широкой публики. Большая часть всей коммуникации в проекте проходит через эти 85 рассылок [ crossref:dev-model[ref-bsd-handbook, FreeBSD, 2003A], Приложение C]. [[tool-pgp]] === Pretty Good Privacy Pretty Good Privacy, более известный как PGP, — это криптосистема, использующая архитектуру открытого ключа, чтобы позволить пользователям подписывать и/или шифровать информацию цифровой подписью для обеспечения безопасной связи между двумя сторонами. Подпись используется при отправке информации множеству получателей, позволяя им убедиться, что информация не была изменена до того, как они её получили. В проекте FreeBSD это основной способ убедиться, что информация была написана тем, кто утверждает, что её создал, и не была изменена в процессе передачи. [[tool-ssh2]] === Secure Shell (SSH) Secure Shell - это стандарт безопасного входа в удалённую систему и выполнения команд на ней. Он позволяет устанавливать и защищать другие соединения, называемые туннелями, между двумя взаимодействующими системами. Этот стандарт существует в двух основных версиях, и только версия два используется в проекте FreeBSD. Наиболее распространённая реализация стандарта - OpenSSH, которая входит в основную дистрибуцию проекта. Поскольку её исходный код обновляется чаще, чем выпуски FreeBSD, последняя версия также доступна в дереве портов. [[sub-projects]] == Подпроекты Подпроекты создаются для уменьшения объёма коммуникации, необходимой для координации группы разработчиков. Когда проблемная область достаточно изолирована, большая часть коммуникации происходит внутри группы, сосредоточенной на проблеме, что требует меньше общения с другими группами по сравнению с ситуацией, когда группа не изолирована. [[sub-project-ports]] === Подпроект Ports "Порт" — это набор метаданных и патчей, необходимых для загрузки, компиляции и корректной установки внешнего программного обеспечения в системе FreeBSD. Количество портов растёт с огромной скоростью, как показано на следующем рисунке. .Количество портов, добавленных между 1995 и 2022 годами [[fig-ports]] image::portsstatus.svg["Обратитесь к параграфам ниже для версии, удобной для экранных дикторов."] crossref:dev-model[fig-ports,image::portsstatus.svg] показывает количество портов, доступных для FreeBSD в период с 1995 по 2022 год. Похоже, что кривая сначала росла экспоненциально, а затем с середины 2001 до середины 2007 года росла линейно со скоростью около 2000 портов/год, после чего скорость роста снизилась. Поскольку внешнее программное обеспечение, описываемое портом, часто находится в стадии активной разработки, объём работы, необходимой для поддержки портов, уже велик и продолжает расти. Это привело к тому, что часть проекта FreeBSD, связанная с портами, получила более самостоятельную структуру и все больше становится подпроектом проекта FreeBSD. Порты имеют свою собственную основную команду с crossref:dev-model[role-ports-manager, Менеджером Портов] во главе, и эта команда может назначать коммиттеров без одобрения Основной команды FreeBSD (Core Team). В отличие от проекта FreeBSD, где активное сопровождение часто вознаграждается правом коммита, подпроект портов включает множество активных сопровождающих, не являющихся коммиттерами. В отличие от основного проекта, дерево портов не разветвляется. Каждый выпуск FreeBSD следует текущей коллекции портов, что обеспечивает доступ к обновлённой информации о том, где найти программы и как их собрать. Однако это означает, что порт, зависящий от системы, может требовать изменений в зависимости от версии FreeBSD, на которой он запущен. С неразветвлённым репозиторием портов невозможно гарантировать, что любой порт будет работать на чём-либо, кроме -CURRENT и -STABLE, в частности на старых, минорных выпусках. Для этого нет ни инфраструктуры, ни времени волонтёров. Команды, зависящие от Ports, такие как команда выпуска релизов, для эффективности коммуникации имеют своих собственных представителей по портам. [[sub-project-documentation]] === Проект документации FreeBSD Проект документации FreeBSD был начат в январе 1995 года. От первоначальной группы, состоявшей из руководителя проекта, четырёх руководителей команд и 16 участников, сейчас общее число коммиттеров достигло 44. Список рассылки документации насчитывает чуть менее 300 участников, что указывает на довольно большое сообщество вокруг него. Цель проекта Документации — предоставить качественную и полезную документацию проекта FreeBSD, чтобы новые пользователи могли легче освоить систему, а также подробно описать расширенные функции для пользователей. Основные задачи проекта Documentation — работа над текущими проектами в "Наборе документации FreeBSD" и перевод документации на другие языки. Как и проект FreeBSD, документация разделена на те же ветви. Это сделано для того, чтобы для каждой версии всегда была обновлённая документация. В ветвях безопасности исправляются только ошибки в документации. -Как и подпроект ports, проект Documentation может назначать коммиттеров документации без одобрения основной команды FreeBSD (Core Team). [crossref:dev-model[freebsd-doceng-charter, FreeBSD, 2003B]]. +Как и подпроект ports, проект Documentation может назначать коммиттеров документации без одобрения основной команды FreeBSD (Core Team). [ crossref:dev-model[freebsd-doceng-charter, FreeBSD, 2003B]]. Проект документации включает в себя extref:{fdp-primer}[вводное руководство]. Оно используется как для ознакомления новых участников проекта со стандартными инструментами и синтаксисом, так и в качестве справочника при работе над проектом. :sectnums!: [bibliography] [[bibliography]] == Список литературы [[brooks]] [Brooks, 1995] Фредерик П. Брукс. Авторское право © 1975, 1995 Pearson Education Limited. 0201835959. Addison-Wesley Pub Co. Мифический человекомесяц. Эссе о программной инженерии, юбилейное издание (2-е издание). [[thesis]] [Saers, 2003] Никлас Саерс. Авторское право © 2003. Модель проекта для FreeBSD. Кандидатская диссертация. http://niklas.saers.com/thesis. [[jorgensen2001]] [Йоргенсен, 2001] Нильс Йоргенсен. Copyright © 2001. _Putting it All in the Trunk. Incremental Software Development in the FreeBSD Open Source Project_. http://www.dat.ruc.dk/~nielsj/research/papers/freebsd.pdf. [[ref-pmbok]] [PMI, 2000] Институт управления проектами. Copyright © 1996, 2000 Институт управления проектами. 1-880410-23-0. Институт управления проектами. Ньютаун Сквер, Пенсильвания, США. PMBOK Guide. A Guide to the Project Management Body of Knowledge (Руководство PMBOK. Руководство к своду знаний по управлению проектами), издание 2000 года. [[freebsd-bylaws]] [FreeBSD, 2000A] Copyright © 2002 The FreeBSD Project. Core Bylaws. https://www.freebsd.org/internal/bylaws/. [[freebsd-developer-handbook]] [FreeBSD, 2002A] Copyright © 2002 The FreeBSD Documentation Project. Руководство FreeBSD для разработчиков. extref:{developers-handbook}[Руководство FreeBSD для разработчиков]. [[bsd-election2002]] [FreeBSD, 2002B] Copyright © 2002 Проект FreeBSD. Выборы состава основной команды (Core Team) 2002. http://election.uk.freebsd.org/candidates.html. [[freebsd-handle-pr]] [FreeBSD, 2002C] Даг-Эрлинг Смёрграв и Хитен Пандья. Copyright © 2002 The FreeBSD Documentation Project. The FreeBSD Documentation Project. Рекомендации по работе с сообщениями о проблемах. extref:{pr-guidelines}[Рекомендации по работе с сообщениями о проблемах]. [[freebsd-send-pr]] [FreeBSD, 2002D] Даг-Эрлинг Смёрграв. Copyright © 2002 Проект документации FreeBSD. Проект документации FreeBSD. Составление сообщений о проблеме во FreeBSD. extref:{problem-reports}[Составление сообщений о проблеме во FreeBSD]. [[freebsd-committer]] [FreeBSD, 2001] Copyright © 2001 The FreeBSD Documentation Project. The FreeBSD Documentation Project. Справочник коммиттера. extref:{committers-guide}[Справочник коммиттера]. [[freebsd-releng]] [FreeBSD, 2002E] Мюррей Стокли. Copyright © 2002 The FreeBSD Documentation Project. Проект документации FreeBSD. Подготовка релизов FreeBSD. extref:{releng}[Подготовка релизов FreeBSD]. [[ref-bsd-handbook]] [FreeBSD, 2003A] Проект документации FreeBSD. Руководство FreeBSD. extref:{handbook}[Руководство FreeBSD]. [[freebsd-contributors]] [FreeBSD, 2002F] Copyright © 2002 The FreeBSD Documentation Project. Проект документации FreeBSD. Участники проекта FreeBSD. extref:{contributors}[Участники проекта FreeBSD]. [[freebsd-election]] [FreeBSD, 2002G] Copyright © 2002 The FreeBSD Project. The FreeBSD Project. Выборы состава основной команды (Core Team) 2002. http://election.uk.freebsd.org. [[freebsd-expiration-policy]] [FreeBSD, 2002H] Copyright © 2002 The FreeBSD Project. The FreeBSD Project. Политика истечения срока действия битов коммитов. 2002/04/06 15:35:30. https://www.freebsd.org/internal/expire-bits/. [[freebsd-new-account]] [FreeBSD, 2002I] Copyright © 2002 The FreeBSD Project. The FreeBSD Project. Процедура создания нового аккаунта. 2002/08/19 17:11:27. https://www.freebsd.org/internal/new-account/. [[freebsd-doceng-charter]] [FreeBSD, 2003B] Copyright © 2002 The FreeBSD Documentation Project. The FreeBSD Documentation Project. Устав команды разработчиков документации FreeBSD. 2003/03/16 12:17. https://www.freebsd.org/internal/doceng/. [[ref-freebsd-trenches]] [Lehey, 2002] Грег Лехи. Copyright © 2002 Грег Лехи. Грег Лехи. Two years in the trenches. The evolution of a software project (Два года в окопах. Эволюция программного проекта). http://www.lemis.com/grog/In-the-trenches.pdf. diff --git a/documentation/content/ru/books/developers-handbook/secure/_index.adoc b/documentation/content/ru/books/developers-handbook/secure/_index.adoc index db6613bffa..2ad0f536ae 100644 --- a/documentation/content/ru/books/developers-handbook/secure/_index.adoc +++ b/documentation/content/ru/books/developers-handbook/secure/_index.adoc @@ -1,229 +1,229 @@ --- authors: - - + - author: 'Murray Stokely' description: 'Безопасное программирование в FreeBSD' next: books/developers-handbook/l10n params: path: /books/developers-handbook/secure/ prev: books/developers-handbook/tools showBookMenu: 'true' tags: ["secure programming", "Buffer Overflows", "SetUID issues"] title: 'Глава 3. Безопасное программирование' weight: 4 --- [[secure]] = Безопасное программирование :doctype: book :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumoffset: 3 :partnums: :source-highlighter: rouge :experimental: :images-path: books/developers-handbook/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] [[secure-synopsis]] == Обзор В этой главе описываются некоторые проблемы безопасности, которые преследуют программистов UNIX(R) на протяжении десятилетий, а также новые инструменты, помогающие избежать написания уязвимого кода. [[secure-philosophy]] == Методология безопасного проектирования Написание безопасных приложений требует очень внимательного и пессимистичного взгляда на жизнь. Приложения должны работать по принципу "наименьших привилегий", чтобы ни один процесс не выполнялся с доступом, превышающим необходимый минимум для выполнения его функций. По возможности следует повторно использовать уже проверенный код, чтобы избежать распространённых ошибок, которые, возможно, уже исправили другие. Одной из ловушек среды UNIX(R) является то, насколько легко делать предположения о разумности окружения. Приложения никогда не должны доверять пользовательскому вводу (во всех его формах), системным ресурсам, межпроцессному взаимодействию или времени событий. Процессы UNIX(R) выполняются не синхронно, поэтому логические операции редко бывают атомарными. [[secure-bufferov]] == Переполнение буфера Переполнение буфера существовало с самых истоков архитектуры фон Неймана crossref:bibliography[COD,1]. Впервые оно получило широкую известность в 1988 году благодаря червю Морриса. К сожалению, эта базовая атака остаётся эффективной и по сей день. Наиболее распространённый тип атаки с переполнением буфера основан на повреждении стека. Большинство современных компьютерных систем используют стек для передачи аргументов процедурам и хранения локальных переменных. Стек — это буфер типа "последним пришёл — первым ушёл" (LIFO) в верхней области памяти процесса. Когда программа вызывает функцию, создаётся новый "стековый кадр". Этот стековый кадр состоит из аргументов, переданных функции, а также динамического количества места для локальных переменных. "Указатель стека" — это регистр, который содержит текущее местоположение вершины стека. Поскольку это значение постоянно меняется по мере добавления новых значений на вершину стека, многие реализации также предоставляют "указатель кадра", который располагается вблизи начала стекового кадра, чтобы локальные переменные могли легче адресоваться относительно этого значения. crossref:bibliography[COD,1] Адрес возврата для вызовов функций также хранится в стеке, и это является причиной эксплойтов переполнения стека, поскольку переполнение локальной переменной в функции может перезаписать адрес возврата этой функции, потенциально позволяя злоумышленнику выполнить любой код по своему усмотрению. Хотя атаки на стек являются наиболее распространёнными, также возможно переполнение стека с помощью атаки на кучу (malloc/free). Язык программирования C не выполняет автоматическую проверку границ массивов или указателей, как это делают многие другие языки. Кроме того, стандартная библиотека C содержит множество очень опасных функций. [.informaltable] [cols="1,1", frame="none"] |=== |`strcpy`(char *dest, const char *src) -| +| Может переполнить буфер назначения |`strcat`(char *dest, const char *src) -| +| Может переполнить буфер назначения |`getwd`(char *buf) -| +| Может переполнить буфер buf |`gets`(char *s) -| +| Может переполнить буфер s |`[vf]scanf`(const char *format, ...) -| +| Может переполнить свои аргументы. |`realpath`(char *path, char resolved_path[]) -| +| Может переполнить буфер пути |`[v]sprintf`(char *str, const char *format, ...) -| +| Может переполнить буфер str. |=== === Пример переполнения буфера Следующий пример кода содержит переполнение буфера, предназначенное для перезаписи адреса возврата и пропуска инструкции, следующей сразу после вызова функции. (Вдохновлено crossref:bibliography[Phrack,4]) [.programlisting] .... #include void manipulate(char *buffer) { char newbuffer[80]; strcpy(newbuffer,buffer); } int main() { char ch,buffer[4096]; int i=0; while ((buffer[i++] = getchar()) != '\n') {}; i=1; manipulate(buffer); i=2; printf("The value of i is : %d\n",i); return 0; } .... Давайте рассмотрим, как будет выглядеть образ памяти этого процесса, если мы введем 160 пробелов в нашу небольшую программу перед нажатием Enter. -[XXX figure here!] +[ XXX figure here! ] Очевидно, что можно разработать более вредоносные входные данные для выполнения реальных скомпилированных инструкций (например, exec(/bin/sh)). === Избегание переполнения буфера Наиболее простое решение проблемы переполнения стека — всегда использовать функции копирования памяти и строк с ограничением длины. `strncpy` и `strncat` являются частью стандартной библиотеки C. Эти функции принимают параметр длины, который не должен превышать размер целевого буфера. Затем эти функции копируют до 'length' байтов из источника в назначение. Однако у этих функций есть ряд проблем. Ни одна из них не гарантирует завершающий NUL, если размер входного буфера равен размеру целевого. Параметр длины также используется неодинаково между `strncpy` и `strncat`, что может сбивать программистов с толку относительно их правильного использования. Также наблюдается значительное снижение производительности по сравнению с `strcpy` при копировании короткой строки в большой буфер, поскольку `strncpy` заполняет оставшееся пространство до указанного размера символами NUL. Существует другая реализация копирования памяти для решения этих проблем. Функции `strlcpy` и `strlcat` гарантируют, что они всегда завершат строку назначения нулевым символом при передаче аргумента ненулевой длины. ==== Скомпилированная проверка границ во время выполнения К сожалению, до сих пор существует очень большое количество кода в открытом доступе, который бездумно копирует память, не используя ни одну из ограниченных функций копирования, которые мы только что обсудили. К счастью, есть способ помочь предотвратить такие атаки — проверка границ во время выполнения, которая реализована в нескольких компиляторах C/C++. ProPolice — это одна из таких функций компилятора, интегрированная в man:gcc[1] версий 4.1 и выше. Она заменяет и расширяет более раннее расширение StackGuard для man:gcc[1]. ProPolice помогает защититься от переполнений буфера на стеке и других атак, размещая псевдослучайные числа в ключевых областях стека перед вызовом любой функции. Когда функция завершается, эти "канарейки" проверяются, и если обнаруживается, что они были изменены, выполнение программы немедленно прекращается. Таким образом, любая попытка изменить адрес возврата или другие переменные, хранящиеся на стеке, с целью запуска вредоносного кода, вряд ли увенчается успехом, так как злоумышленнику также необходимо оставить псевдослучайные канарейки нетронутыми. Перекомпиляция вашего приложения с использованием ProPolice является эффективным способом предотвращения большинства атак, связанных с переполнением буфера, но оно всё ещё может быть скомпрометировано. ==== Библиотечная проверка границ во время выполнения Механизмы на основе компилятора совершенно бесполезны для проприетарного программного обеспечения, которое невозможно перекомпилировать. Для таких ситуаций существует ряд библиотек, которые переопределяют небезопасные функции стандартной библиотеки C (`strcpy`, `fscanf`, `getwd` и т.д.) и гарантируют, что эти функции никогда не смогут записать данные за указатель стека. * libsafe * libverify * libparanoia К сожалению, эти защиты на основе библиотек имеют ряд недостатков. Они защищают лишь от очень небольшого набора проблем, связанных с безопасностью, и не устраняют основную причину. Эти защиты могут не сработать, если приложение было скомпилировано с флагом -fomit-frame-pointer. Кроме того, переменные окружения LD_PRELOAD и LD_LIBRARY_PATH могут быть перезаписаны или сброшены пользователем. [[secure-setuid]] == Проблемы с SetUID Существует как минимум 6 различных идентификаторов, связанных с каждым процессом, поэтому необходимо очень внимательно следить за уровнем доступа вашего процесса в любой момент времени. В частности, все приложения с seteuid должны отказываться от своих привилегий, как только в них больше нет необходимости. Действительный идентификатор пользователя может быть изменён только процессом с правами суперпользователя. Программа login устанавливает его при первоначальном входе пользователя в систему, и он редко изменяется. Эффективный идентификатор пользователя устанавливается функциями `exec()`, если у программы установлен бит seteuid. Приложение может вызывать `seteuid()` в любое время, чтобы установить эффективный идентификатор пользователя либо в реальный идентификатор пользователя, либо в сохранённый set-user-ID. Когда эффективный идентификатор пользователя устанавливается функциями `exec()`, предыдущее значение сохраняется в сохранённом set-user-ID. [[secure-chroot]] == Ограничение окружения вашей программы Традиционный метод ограничения процесса — это системный вызов `chroot()`. Этот системный вызов изменяет корневой каталог, от которого ссылаются все остальные пути для процесса и любых дочерних процессов. Для успешного выполнения этого вызова процесс должен иметь право на выполнение (поиск) в указанном каталоге. Новая среда фактически не вступает в силу, пока вы не выполните `chdir()` в новой среде. Также следует отметить, что процесс может легко выйти из окружения chroot, если он имеет привилегии root. Это может быть достигнуто путем создания узлов устройств для чтения памяти ядра, подключения отладчика к процессу вне окружения man:chroot[8] или многими другими творческими способами. Поведение системного вызова `chroot()` можно частично контролировать с помощью переменной `sysctl` kern.chroot_allow_open_directories. Если этому параметру присвоено значение 0, `chroot()` завершится с ошибкой EPERM, если есть какие-либо открытые каталоги. Если установлено значение по умолчанию 1, то `chroot()` завершится с ошибкой EPERM, если есть открытые каталоги и процесс уже находится внутри вызова `chroot()`. Для любого другого значения проверка на открытые каталоги будет полностью пропущена. === Функциональность клеток FreeBSD Концепция `клетки` расширяет возможности `chroot()`, ограничивая права суперпользователя для создания настоящего `виртуального сервера`. После настройки клетки все сетевые взаимодействия должны осуществляться через указанный IP-адрес, а привилегии `root` внутри этой клетки сильно ограничены. Находясь в клетке, любые проверки прав суперпользователя в ядре с использованием вызова `suser()` завершатся неудачей. Однако некоторые вызовы `suser()` были заменены на новый интерфейс `suser_xxx()`. Эта функция отвечает за распознавание или запрет доступа к правам суперпользователя для процессов в клетке. Суперпользователь в среде клетки имеет возможность: * Управлять учётными данными с помощью `setuid`, `seteuid`, `setgid`, `setegid`, `setgroups`, `setreuid`, `setregid`, `setlogin` * Устанавливать ограничений ресурсов с помощью `setrlimit` * Изменять некоторые узлы sysctl (kern.hostname) * `chroot()` * Устанавливать флаги на vnode: `chflags`, `fchflags` * Устанавливать атрибуты vnode, такие как права доступа к файлу, владелец, группа, размер, время доступа и время изменения. * Привязываться к привилегированным портам в домене Интернета (порты < 1024) `Клетка` — это очень полезный инструмент для запуска приложений в безопасной среде, но у него есть некоторые недостатки. В настоящее время механизмы IPC не были преобразованы в `suser_xxx`, поэтому такие приложения, как MySQL, не могут быть запущены внутри клетки. Доступ суперпользователя может иметь очень ограниченное значение внутри клетки, но нет возможности точно указать, что означает «очень ограниченный». === Возможности процесса в POSIX(R).1e POSIX(R) выпустил рабочий проект, который добавляет аудит событий, списки контроля доступа, детализированные привилегии, маркировку информации и обязательный контроль доступа. Это работа в процессе, и она является основным направлением проекта http://www.trustedbsd.org/[TrustedBSD]. Некоторые первоначальные наработки были добавлены в FreeBSD-CURRENT (cap_set_proc(3)). [[secure-trust]] == Доверие Приложение никогда не должно предполагать, что окружение пользователя является предсказуемым. Это включает (но не ограничивается): пользовательский ввод, сигналы, переменные окружения, ресурсы, IPC, mmaps, текущий рабочий каталог файловой системы, файловые дескрипторы, количество открытых файлов и т.д. Никогда не следует предполагать, что можно отловить все виды некорректных входных данных, которые может предоставить пользователь. Вместо этого ваше приложение должно использовать позитивную фильтрацию, разрешая только определённое подмножество входных данных, которые вы считаете безопасными. Некорректная проверка данных стала причиной многих уязвимостей, особенно в CGI-скриптах во всемирной паутине. Для имён файлов необходимо быть особенно осторожными с путями ("../", "/"), символическими ссылками и escape-символами оболочки. В Perl есть замечательная функция под названием "Режим Taint", которая может использоваться для предотвращения небезопасного использования данных, полученных извне программы. Этот режим проверяет аргументы командной строки, переменные окружения, информацию о локали, результаты определённых системных вызовов (`readdir()`, `readlink()`, `getpwxxx()`) и все вводимые данные из файлов. [[secure-race-conditions]] == Состояние гонки Состояние гонки — это аномальное поведение, вызванное непредвиденной зависимостью от относительного времени событий. Другими словами, программист ошибочно предположил, что определённое событие всегда произойдет раньше другого. Некоторые из распространённых причин состояний гонки — это сигналы, проверки доступа и открытие файлов. Сигналы по своей природе являются асинхронными событиями, поэтому при работе с ними необходимо проявлять особую осторожность. Проверка доступа с помощью `access(2)`, а затем `open(2)` явно неатомарна. Пользователи могут перемещать файлы между этими двумя вызовами. Вместо этого привилегированные приложения должны использовать `seteuid()`, а затем вызывать `open()` напрямую. По аналогии, приложение всегда должно устанавливать правильную маску (`umask`) перед вызовом `open()`, чтобы избежать необходимости в лишних вызовах `chmod()`. diff --git a/documentation/content/ru/books/handbook/advanced-networking/_index.adoc b/documentation/content/ru/books/handbook/advanced-networking/_index.adoc index de1ab8a518..7dece84b45 100644 --- a/documentation/content/ru/books/handbook/advanced-networking/_index.adoc +++ b/documentation/content/ru/books/handbook/advanced-networking/_index.adoc @@ -1,2063 +1,2063 @@ --- description: 'Сложные вопросы работы в сети в FreeBSD: основы шлюзов и маршрутов, CARP, настройка нескольких VLAN в FreeBSD и так далее' next: books/handbook/partv params: path: /books/handbook/advanced-networking/ part: 'IV. Сетевое взаимодействие' prev: books/handbook/firewalls showBookMenu: 'true' tags: ["Advanced Networking", "Handbook", "gateway", "routes", "wireless", "tethering", "bluetooth", "bridging", "CARP", "VLAN", "WiFi"] title: 'Глава 34. Сложные вопросы работы в сети' weight: 39 --- [[advanced-networking]] = Сложные вопросы работы в сети :doctype: book :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumoffset: 34 :partnums: :source-highlighter: rouge :experimental: :images-path: books/handbook/advanced-networking/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] [[advanced-networking-synopsis]] == Обзор Эта глава охватывает ряд сложных тем, связанных с сетями. Прочтите эту главу, чтобы узнать: * Основы шлюзов и маршрутов. * Как настроить USB-тетеринг. * Как настроить устройства IEEE(R) 802.11 и Bluetooth(R). * Как сделать так, чтобы система FreeBSD работала как мост. * Как настроить загрузку системы из сети с помощью PXE. * Как включить и использовать возможности протокола Common Address Redundancy Protocol (CARP) в FreeBSD. * Как настроить несколько VLAN в FreeBSD. * Как настроить гарнитуру Bluetooth. Прежде чем читать эту главу, необходимо: * Понимать основы скриптов [.filename]#/etc/rc#. * Знать основные термины и понятия сетевых технологий. * Понимать базовые настройки сети в FreeBSD (crossref:network[network,Сеть FreeBSD]). * Знать, как настроить и установить новое ядро FreeBSD (crossref:kernelconfig[kernelconfig,Настройка ядра FreeBSD]). * Знать, как устанавливать дополнительное стороннее программное обеспечение (crossref:ports[ports,Установка приложений: Пакеты и Порты]). [[network-routing]] == Шлюзы и маршруты _Маршрутизация_ — это механизм, позволяющий системе находить сетевой путь к другой системе. _Маршрут_ — это определённая пара адресов, представляющих "назначение" и "шлюз". Маршрут указывает, что при попытке достичь указанного назначения пакеты должны отправляться через указанный шлюз. Существует три типа назначений: отдельные хосты, подсети и "маршрут по умолчанию". "Маршрут по умолчанию" используется, если не подходит ни один другой маршрут. Также существует три типа шлюзов: отдельные хосты, интерфейсы (также называемые линками) и аппаратные (MAC) адреса Ethernet. Известные маршруты хранятся в таблице маршрутизации. В этом разделе представлен обзор основ маршрутизации. Затем показано, как настроить систему FreeBSD в качестве маршрутизатора, и даны некоторые советы по устранению неполадок. [[network-routing-default]] === Основы маршрутизации Для просмотра таблицы маршрутизации системы FreeBSD используйте man:netstat[1]: [source, shell] .... % netstat -r Routing tables Internet: Destination Gateway Flags Refs Use Netif Expire default outside-gw UGS 37 418 em0 localhost localhost UH 0 181 lo0 test0 0:e0:b5:36:cf:4f UHLW 5 63288 re0 77 10.20.30.255 link#1 UHLW 1 2421 example.com link#1 UC 0 0 host1 0:e0:a8:37:8:1e UHLW 3 4601 lo0 host2 0:e0:a8:37:8:1e UHLW 0 5 lo0 => host2.example.com link#1 UC 0 0 224 link#1 UC 0 0 .... Записи в этом примере следующие: default:: Первый маршрут в этой таблице указывает маршрут по умолчанию (`default`). Когда локальной системе требуется установить соединение с удалённым узлом, она проверяет таблицу маршрутизации, чтобы определить, существует ли известный путь. Если удалённый узел соответствует записи в таблице, система проверяет, может ли она подключиться, используя интерфейс, указанный в этой записи. + Если назначение не соответствует ни одной записи или если все известные пути недоступны, система использует запись для маршрута по умолчанию. Для хостов в локальной сети поле `Gateway` в маршруте по умолчанию указывает на систему, имеющую прямое подключение к Интернету. При чтении этой записи убедитесь, что столбец `Flags` указывает на то, что шлюз доступен (`UG`). + Маршрут по умолчанию для машины, которая сама функционирует как шлюз во внешний мир, будет шлюзом провайдера интернет-услуг (ISP). localhost:: Второй маршрут — это маршрут `localhost`. Интерфейс, указанный в столбце `Netif` для `localhost`, — это [.filename]#lo0#, также известное как loopback-устройство. Это означает, что весь трафик для этого назначения должен быть внутренним, а не отправляться через сеть. MAC адрес:: Адреса, начинающиеся с `0:e0:`, являются MAC-адресами. FreeBSD автоматически определит любые хосты, например `test0`, в локальной сети Ethernet и добавит маршрут для этого хоста через интерфейс Ethernet [.filename]#re0#. Такой маршрут имеет время жизни, указанное в столбце `Expire`, которое используется, если хост не отвечает в течение определённого времени. В этом случае маршрут к этому хосту будет автоматически удалён. Эти хосты определяются с помощью Протокола маршрутной информации (RIP — Routing Information Protocol), который вычисляет маршруты к локальным хостам на основе определения кратчайшего пути. subnet:: FreeBSD автоматически добавит маршруты для локальной подсети. В этом примере `10.20.30.255` — это широковещательный адрес для подсети `10.20.30`, а `example.com` — доменное имя, связанное с этой подсетью. Обозначение `link#1` относится к первой Ethernet-карте в машине. + Локальные хосты сети и локальные подсети автоматически получают маршруты через демон man:routed[8]. Если он не запущен, будут существовать только маршруты, статически определённые администратором. host:: Строка `host1` ссылается на хост по его Ethernet-адресу. Поскольку это отправляющий хост, FreeBSD использует loopback-интерфейс ([.filename]#lo0#) вместо Ethernet-интерфейса. + Две строки `host2` представляют собой псевдонимы, созданные с помощью man:ifconfig[8]. Символ `=>` после интерфейса [.filename]#lo0# указывает, что помимо loopback-адреса был установлен псевдоним. Такие маршруты отображаются только на хосте, поддерживающем псевдоним, а все остальные хосты в локальной сети будут иметь строку `link#1` для таких маршрутов. 224:: Последняя строка (подсеть назначения `224`) относится к многоадресной рассылке. Различные атрибуты каждого маршрута можно увидеть в столбце `Flags`. crossref:advanced-networking[routeflags,Часто встречающиеся флаги таблицы маршрутизации] содержит сводку некоторых из этих флагов и их значений: [[routeflags]] .Часто встречающиеся флаги таблицы маршрутизации [cols="1,1", frame="none", options="header"] |=== | Flag | Назначение |U |Маршрут активен (поднят). |H |Целью маршрута является отдельный хост. |G |Отправляйте всё для этого назначения на этот шлюз, который разберётся, куда это нужно отправить. |S |Этот маршрут был настроен статически. |C |Клонирует новый маршрут на основе данного для подключения машин. Такой тип маршрута обычно используется для локальных сетей. |W |Маршрут был автоматически настроен на основе локальной сети (клон) маршрута. |L |Маршрут включает ссылки на оборудование Ethernet (link). |=== На системе FreeBSD маршрут по умолчанию может быть определён в [.filename]#/etc/rc.conf# путём указания IP-адреса шлюза по умолчанию: [.programlisting] .... defaultrouter="10.20.30.1" .... Также можно вручную добавить маршрут с помощью `route`: [source, shell] .... # route add default 10.20.30.1 .... Обратите внимание, что вручную добавленные маршруты не сохранятся после перезагрузки. Для получения дополнительной информации о ручном управлении таблицами сетевой маршрутизации обратитесь к man:route[8]. [[network-static-routes]] === Настройка маршрутизатора со статическими маршрутами Система FreeBSD может быть настроена как шлюз по умолчанию или маршрутизатор для сети, если она является двухдоменной системой. Двухдоменная система — это хост, который находится как минимум в двух разных сетях. Обычно каждая сеть подключена к отдельному сетевому интерфейсу, хотя IP-алиасинг может использоваться для привязки нескольких адресов, каждый в своей подсети, к одному физическому интерфейсу. Для того чтобы система могла пересылать пакеты между интерфейсами, FreeBSD должна быть настроена как маршрутизатор. Интернет-стандарты и лучшие инженерные практики не позволяют проекту FreeBSD включать эту функцию по умолчанию, но её можно настроить для запуска при загрузке, добавив следующую строку в [.filename]#/etc/rc.conf#: [.programlisting] .... gateway_enable="YES" # Set to YES if this host will be a gateway .... Чтобы теперь включить маршрутизацию, установите переменную man:sysctl[8] `net.inet.ip.forwarding` в значение `1`. Для отключения маршрутизации сбросьте эту переменную в `0`. Таблица маршрутизации маршрутизатора требует дополнительных маршрутов, чтобы он знал, как достичь других сетей. Маршруты могут быть добавлены вручную с использованием статических маршрутов или могут быть автоматически созданы обучением с помощью протокола маршрутизации. Статические маршруты подходят для небольших сетей, и в этом разделе описывается, как добавить запись статической маршрутизации для небольшой сети. [NOTE] ==== Для больших сетей статические маршруты быстро становятся неэффективными. FreeBSD включает стандартный демон маршрутизации man:routed[8], который поддерживает протоколы RIP версий 1 и 2, а также IRDP. Поддержка протоколов маршрутизации BGP и OSPF может быть установлена с помощью пакета package:net/quagga[] или порта. ==== Рассмотрим следующую сеть: image::static-routes.png[] В этом сценарии `RouterA` — это машина FreeBSD, которая выступает в качестве маршрутизатора для остальной части Интернета. У неё установлен маршрут по умолчанию на `10.0.0.1`, что позволяет ей соединяться с внешним миром. `RouterB` уже настроен на использование `192.168.1.1` в качестве шлюза по умолчанию. Прежде чем добавлять статические маршруты, таблица маршрутизации на `RouterA` выглядит следующим образом: [source, shell] .... % netstat -nr Routing tables Internet: Destination Gateway Flags Refs Use Netif Expire default 10.0.0.1 UGS 0 49378 xl0 127.0.0.1 127.0.0.1 UH 0 6 lo0 10.0.0.0/24 link#1 UC 0 0 xl0 192.168.1.0/24 link#2 UC 0 0 xl1 .... С текущей таблицей маршрутизации `RouterA` не имеет маршрута к сети `192.168.2.0/24`. Следующая команда добавляет сеть `Internal Net 2` в таблицу маршрутизации ``RouterA``, используя `192.168.1.2` в качестве следующего прыжка: [source, shell] .... # route add -net 192.168.2.0/24 192.168.1.2 .... Теперь `RouterA` может достигать любого узла в сети `192.168.2.0/24`. Однако информация о маршрутизации не сохранится после перезагрузки системы FreeBSD. Если требуется, чтобы статический маршрут был постоянным, добавьте его в [.filename]#/etc/rc.conf#: [.programlisting] .... # Add Internal Net 2 as a persistent static route static_routes="internalnet2" route_internalnet2="-net 192.168.2.0/24 192.168.1.2" .... Переменная конфигурации `static_routes` представляет собой список строк, разделённых пробелом, где каждая строка ссылается на имя маршрута. Переменная `route_internalnet2` содержит статический маршрут для этого имени маршрута. Использование более одной строки в `static_routes` создаёт несколько статических маршрутов. Ниже приведен пример добавления статических маршрутов для сетей `192.168.0.0/24` и `192.168.1.0/24`: [.programlisting] .... static_routes="net1 net2" route_net1="-net 192.168.0.0/24 192.168.0.1" route_net2="-net 192.168.1.0/24 192.168.1.1" .... [[network-routing-troubleshooting]] === Устранение неполадок Когда адресное пространство назначается сети, поставщик услуг настраивает свои таблицы маршрутизации так, чтобы весь трафик для сети отправлялся по каналу связи к сайту. Но как внешние сайты узнают, что их пакеты нужно отправлять к межсетевому экрану провайдера сети? Существует система, которая отслеживает все выделенные адресные пространства и определяет их точку подключения к магистрали Интернета или основным магистральным линиям, передающим интернет-трафик по стране и по всему миру. Каждая машина магистрали имеет копию главного набора таблиц, которые направляют трафик для определённой сети к конкретному магистральному оператору, а оттуда по цепочке поставщиков услуг, пока он не достигнет конкретной сети. Это задача поставщика услуг — сообщить магистральным узлам, что они являются точкой подключения и, следовательно, путем внутрь для сайта. Это известно как распространение маршрутов. Иногда возникают проблемы с распространением маршрутов, и некоторые сайты не могут подключиться. Возможно, наиболее полезная команда для выяснения, где происходит разрыв маршрутизации, — это `traceroute`. Она полезна, когда `ping` не срабатывает. При использовании `traceroute` укажите адрес удалённого хоста для подключения. В выводе будут показаны шлюзы на пути попытки соединения, в конечном итоге достигая целевого хоста или прерываясь из-за отсутствия соединения. Для получения дополнительной информации обратитесь к man:traceroute[8]. [[network-routing-multicast]] === Аспекты многоадресной рассылки (multicast) FreeBSD изначально поддерживает как приложения с многоадресной рассылкой, так и маршрутизацию многоадресной рассылки. Для работы приложений с многоадресной рассылкой на FreeBSD не требуется специальной настройки. Для поддержки маршрутизации многоадресной рассылки необходимо включить следующую опцию в собственном ядре: [.programlisting] .... options MROUTING .... Демон маршрутизации многоадресной рассылки, mrouted, может быть установлен с помощью пакета package:net/mrouted[] или порта. Этот демон реализует протокол маршрутизации многоадресной рассылки DVMRP и настраивается путём редактирования файла [.filename]#/usr/local/etc/mrouted.conf# для настройки туннелей и DVMRP. Установка mrouted также устанавливает map-mbone и mrinfo, а также связанные с ними man-страницы. Обратитесь к ним за примерами конфигурации. [NOTE] ==== DVMRP во многом заменён протоколом PIM во многих инсталляциях с использованием многоадресной рассылки. Дополнительную информацию можно найти в man:pim[4]. ==== [[configtuning-virtual-hosts]] == Виртуальные узлы Распространённое использование FreeBSD — это виртуальный хостинг сайтов, когда один сервер представляется в сети как множество серверов. Это достигается путём назначения нескольких сетевых адресов одному интерфейсу. Указанный сетевой интерфейс имеет один "реальный" адрес и может иметь любое количество "псевдонимных" адресов. Эти псевдонимы обычно добавляются путём размещения записей alias в [.filename]#/etc/rc.conf#, как показано в этом примере: [source, shell] .... # sysrc ifconfig_fxp0_alias0="inet xxx.xxx.xxx.xxx netmask xxx.xxx.xxx.xxx" .... Записи псевдонимов должны начинаться с `alias__0__`, используя последовательные числа, такие как `alias0`, `alias1` и так далее. Процесс настройки остановится при первом пропущенном числе. Расчёт масок подсети для псевдонимов важен. Для заданного интерфейса должен быть один адрес, который корректно представляет маску подсети сети. Любые другие адреса, попадающие в эту сеть, должны иметь маску подсети, состоящую из всех ``1``, выраженную как `255.255.255.255` или `0xffffffff`. Например, рассмотрим случай, когда интерфейс `fxp0` подключён к двум сетям: `10.1.1.0` с маской сети `255.255.255.0` и `202.0.75.16` с маской сети `255.255.255.240`. Система должна быть настроена так, чтобы находиться в диапазонах `10.1.1.1`–`10.1.1.5` и `202.0.75.17`–`202.0.75.20`. Только первый адрес в каждом диапазоне должен иметь реальную маску сети. Все остальные (`10.1.1.2`–`10.1.1.5` и `202.0.75.18`–`202.0.75.20`) должны быть настроены с маской `255.255.255.255`. Для данного сценария правильно настраивают адаптер следующие записи в [.filename]#/etc/rc.conf# : [source, shell] .... # sysrc ifconfig_fxp0="inet 10.1.1.1 netmask 255.255.255.0" # sysrc ifconfig_fxp0_alias0="inet 10.1.1.2 netmask 255.255.255.255" # sysrc ifconfig_fxp0_alias1="inet 10.1.1.3 netmask 255.255.255.255" # sysrc ifconfig_fxp0_alias2="inet 10.1.1.4 netmask 255.255.255.255" # sysrc ifconfig_fxp0_alias3="inet 10.1.1.5 netmask 255.255.255.255" # sysrc ifconfig_fxp0_alias4="inet 202.0.75.17 netmask 255.255.255.240" # sysrc ifconfig_fxp0_alias5="inet 202.0.75.18 netmask 255.255.255.255" # sysrc ifconfig_fxp0_alias6="inet 202.0.75.19 netmask 255.255.255.255" # sysrc ifconfig_fxp0_alias7="inet 202.0.75.20 netmask 255.255.255.255" .... Более простой способ выразить это — использовать список диапазонов IP-адресов, разделённых пробелами. Первому адресу будет назначена указанная маска подсети, а дополнительным адресам — маска подсети `255.255.255.255`. [source, shell] .... # sysrc ifconfig_fxp0_aliases="inet 10.1.1.1-5/24 inet 202.0.75.17-20/28" .... [[network-advanced-wireless]] == Расширенная аутентификация в беспроводной сети FreeBSD поддерживает различные способы подключения к беспроводной сети. В этом разделе описано, как выполнить расширенную аутентификацию в беспроводной сети. Для подключения и базовой аутентификации в беспроводной сети раздел crossref:network[wireless-authentication,Подключение и аутентификация в беспроводной сети] в главе "Сеть" описывает, как это сделать. [[network-wireless-wpa-eap-tls]] === WPA с EAP-TLS Второй способ использования WPA — с сервером аутентификации 802.1X. В этом случае WPA называется WPA Enterprise, чтобы отличать его от менее безопасного WPA Personal. Аутентификация в WPA Enterprise основана на расширяемом протоколе аутентификации (EAP). EAP не включает в себя метод шифрования. Вместо этого EAP встраивается в зашифрованный туннель. Существует множество методов аутентификации EAP, но наиболее распространены EAP-TLS, EAP-TTLS и EAP-PEAP. EAP с защитой на транспортном уровне (EAP-TLS) — это широко поддерживаемый протокол аутентификации беспроводных сетей, так как он был первым методом EAP, сертифицированным http://www.wi-fi.org/[Альянсом Wi-Fi]. Для работы EAP-TLS требуется три сертификата: сертификат центра сертификации (CA), установленный на всех машинах, сертификат сервера для сервера аутентификации и один клиентский сертификат для каждого беспроводного клиента. В этом методе EAP и сервер аутентификации, и беспроводной клиент аутентифицируют друг друга, предоставляя свои соответствующие сертификаты, а затем проверяют, что эти сертификаты были подписаны CA организации. Как и ранее, настройка выполняется через [.filename]#/etc/wpa_supplicant.conf#: [.programlisting] .... network={ ssid="freebsdap" <.> proto=RSN <.> key_mgmt=WPA-EAP <.> eap=TLS <.> identity="loader" <.> ca_cert="/etc/certs/cacert.pem" <.> client_cert="/etc/certs/clientcert.pem" <.> private_key="/etc/certs/clientkey.pem" <.> private_key_passwd="freebsdmallclient" <.> } .... <.> Это поле указывает имя сети (SSID). <.> Этот пример использует протокол RSN IEEE(R) 802.11i, также известный как WPA2. <.> Строка `key_mgmt` указывает на используемый протокол управления ключами. В данном примере это WPA с аутентификацией EAP. <.> Это поле указывает метод EAP для подключения. <.> Поле `identity` содержит строку идентификации для EAP. <.> Поле `ca_cert` указывает путь к файлу сертификата CA. Этот файл необходим для проверки сертификата сервера. <.> Строка `client_cert` указывает путь к файлу сертификата клиента. Этот сертификат уникален для каждого беспроводного клиента в сети. <.> Поле `private_key` содержит путь к файлу закрытого ключа клиентского сертификата. <.> Поле `private_key_passwd` содержит парольную фразу для закрытого ключа. Затем добавьте следующие строки в [.filename]#/etc/rc.conf#: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="WPA DHCP" .... Следующий шаг — поднять интерфейс: [source, shell] .... # service netif start Starting wpa_supplicant. DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 7 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 15 DHCPACK from 192.168.0.20 bound to 192.168.0.254 -- renewal in 300 seconds. wlan0: flags=8843 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.254 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet DS/11Mbps mode 11g status: associated ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 3:128-bit txpower 21.5 bmiss 7 scanvalid 450 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst roaming MANUAL .... Также можно поднять интерфейс вручную с помощью man:wpa_supplicant[8] и man:ifconfig[8]. [[network-wireless-wpa-eap-ttls]] === WPA с EAP-TTLS С EAP-TLS и сервер аутентификации, и клиент нуждаются в сертификате. С EAP-TTLS сертификат клиента необязателен. Этот метод аналогичен веб-серверу, который создаёт защищенный SSL-туннель, даже если у посетителей нет клиентских сертификатов. EAP-TTLS использует зашифрованный TLS-туннель для безопасной передачи данных аутентификации. Требуемая конфигурация может быть добавлена в [.filename]#/etc/wpa_supplicant.conf#: [.programlisting] .... network={ ssid="freebsdap" proto=RSN key_mgmt=WPA-EAP eap=TTLS <.> identity="test" <.> password="test" <.> ca_cert="/etc/certs/cacert.pem" <.> phase2="auth=MD5" <.> } .... <.> Это поле определяет метод EAP для подключения. <.> Поле `identity` содержит строку идентификации для аутентификации EAP внутри зашифрованного TLS-туннеля. <.> Поле `password` содержит парольную фразу для аутентификации EAP. <.> Поле `ca_cert` указывает путь к файлу сертификата CA. Этот файл необходим для проверки сертификата сервера. <.> Это поле определяет метод аутентификации, используемый в зашифрованном TLS-туннеле. В данном примере используется EAP с MD5-Challenge. Фаза "внутренней аутентификации" часто называется "phase2". Далее добавьте следующие строки в [.filename]#/etc/rc.conf#: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="WPA DHCP" .... Следующий шаг — поднять интерфейс: [source, shell] .... # service netif start Starting wpa_supplicant. DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 7 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 15 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 21 DHCPACK from 192.168.0.20 bound to 192.168.0.254 -- renewal in 300 seconds. wlan0: flags=8843 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.254 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet DS/11Mbps mode 11g status: associated ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 3:128-bit txpower 21.5 bmiss 7 scanvalid 450 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst roaming MANUAL .... [[network-wireless-wpa-eap-peap]] === WPA с EAP-PEAP [NOTE] ==== PEAPv0/EAP-MSCHAPv2 является наиболее распространённым методом PEAP. В этой главе термин PEAP используется для обозначения данного метода. ==== Защищенный EAP (PEAP) разработан как альтернатива EAP-TTLS и является наиболее используемым стандартом EAP после EAP-TLS. В сети с разными операционными системами PEAP должен быть наиболее поддерживаемым стандартом после EAP-TLS. PEAP аналогичен EAP-TTLS, так как использует сертификат на стороне сервера для аутентификации клиентов путем создания зашифрованного TLS-туннеля между клиентом и сервером аутентификации, что защищает последующий обмен аутентификационной информацией. Аутентификация PEAP отличается от EAP-TTLS тем, что передаёт имя пользователя в открытом виде, и только пароль отправляется в зашифрованном TLS-туннеле. EAP-TTLS использует TLS-туннель как для имени пользователя, так и для пароля. Добавьте следующие строки в [.filename]#/etc/wpa_supplicant.conf# для настройки параметров, связанных с EAP-PEAP: [.programlisting] .... network={ ssid="freebsdap" proto=RSN key_mgmt=WPA-EAP eap=PEAP <.> identity="test" <.> password="test" <.> ca_cert="/etc/certs/cacert.pem" <.> phase1="peaplabel=0" <.> phase2="auth=MSCHAPV2" <.> } .... <.> Это поле определяет метод EAP для подключения. <.> Поле `identity` содержит строку идентификации для аутентификации EAP внутри зашифрованного TLS-туннеля. <.> Поле `password` содержит парольную фразу для аутентификации EAP. <.> Поле `ca_cert` указывает путь к файлу сертификата CA. Этот файл необходим для проверки сертификата сервера. <.> Это поле содержит параметры для первой фазы аутентификации, TLS-туннеля. В зависимости от используемого сервера аутентификации укажите конкретную метку для аутентификации. В большинстве случаев меткой будет "client EAP encryption", которая устанавливается с помощью `peaplabel=0`. Дополнительную информацию можно найти в man:wpa_supplicant.conf[5]. <.> Это поле определяет протокол аутентификации, используемый в зашифрованном TLS-туннеле. В случае PEAP, это `auth=MSCHAPV2`. Добавьте следующее в [.filename]#/etc/rc.conf#: [.programlisting] .... wlans_ath0="wlan0" ifconfig_wlan0="WPA DHCP" .... Затем поднимите интерфейс: [source, shell] .... # service netif start Starting wpa_supplicant. DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 7 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 15 DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 21 DHCPACK from 192.168.0.20 bound to 192.168.0.254 -- renewal in 300 seconds. wlan0: flags=8843 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.254 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet DS/11Mbps mode 11g status: associated ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 3:128-bit txpower 21.5 bmiss 7 scanvalid 450 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst roaming MANUAL .... [[wireless-ad-hoc-mode]] == Беспроводное соединение в режиме Ad-hoc Режим IBSS, также называемый ad-hoc режимом, предназначен для соединений точка-точка. Например, чтобы создать ad-hoc сеть между машинами `A` и `B`, выберите два IP-адреса и SSID. На `A`: [source, shell] .... # ifconfig wlan0 create wlandev ath0 wlanmode adhoc # ifconfig wlan0 inet 192.168.0.1 netmask 255.255.255.0 ssid freebsdap # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether 00:11:95:c3:0d:ac inet 192.168.0.1 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet autoselect mode 11g status: running ssid freebsdap channel 2 (2417 Mhz 11g) bssid 02:11:95:c3:0d:ac country US ecm authmode OPEN privacy OFF txpower 21.5 scanvalid 60 protmode CTS wme burst .... Параметр `adhoc` указывает, что интерфейс работает в режиме IBSS. `B` теперь должен иметь возможность обнаруживать `A`: [source, shell] .... # ifconfig wlan0 create wlandev ath0 wlanmode adhoc # ifconfig wlan0 up scan SSID/MESH ID BSSID CHAN RATE S:N INT CAPS freebsdap 02:11:95:c3:0d:ac 2 54M -64:-96 100 IS WME .... `I` в выводе подтверждает, что `A` находится в режиме ad-hoc. Теперь настройте `B` с другим IP-адресом: [source, shell] .... # ifconfig wlan0 inet 192.168.0.2 netmask 255.255.255.0 ssid freebsdap # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.2 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet autoselect mode 11g status: running ssid freebsdap channel 2 (2417 Mhz 11g) bssid 02:11:95:c3:0d:ac country US ecm authmode OPEN privacy OFF txpower 21.5 scanvalid 60 protmode CTS wme burst .... Оба `A` и `B` теперь готовы обмениваться информацией. [[network-wireless-ap]] === Хост FreeBSD в роли точки доступа FreeBSD может функционировать как точка доступа (AP), что устраняет необходимость покупки аппаратной точки доступа или организации ad-hoc сети. Это может быть особенно полезно, когда машина FreeBSD выступает в качестве шлюза к другой сети, например, к Интернету. [[network-wireless-ap-basic]] ==== Основные настройки Прежде чем настраивать машину FreeBSD в качестве точки доступа, ядро должно быть сконфигурировано с соответствующей поддержкой сети для беспроводной карты, а также используемых протоколов безопасности. Для получения дополнительной информации см. crossref:advanced-networking[network-wireless-ap-basic, Базовые настройки]. [NOTE] ==== Драйвер-оболочка NDIS для драйверов Windows(R) в настоящее время не поддерживает работу в режиме точки доступа. Только родные беспроводные драйверы FreeBSD поддерживают режим AP. ==== После загрузки поддержки беспроводной сети проверьте, поддерживает ли беспроводное устройство режим точки доступа на основе хоста, также известный как режим hostap: [source, shell] .... # ifconfig wlan0 create wlandev ath0 # ifconfig wlan0 list caps drivercaps=6f85edc1 cryptocaps=1f .... Этот вывод показывает возможности карты. Слово `HOSTAP` подтверждает, что эта беспроводная карта может работать как точка доступа. Также перечислены различные поддерживаемые алгоритмы шифрования: WEP, TKIP и AES. Эта информация указывает, какие протоколы безопасности можно использовать на точке доступа. Беспроводное устройство можно перевести в режим hostap только во время создания сетевого псевдоустройства, поэтому ранее созданное устройство необходимо сначала удалить: [source, shell] .... # ifconfig wlan0 destroy .... затем повторно создать с правильной опцией перед установкой остальных параметров: [source, shell] .... # ifconfig wlan0 create wlandev ath0 wlanmode hostap # ifconfig wlan0 inet 192.168.0.1 netmask 255.255.255.0 ssid freebsdap mode 11g channel 1 .... Используйте man:ifconfig[8] снова, чтобы посмотреть состояние интерфейса [.filename]#wlan0#: [source, shell] .... # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether 00:11:95:c3:0d:ac inet 192.168.0.1 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet autoselect mode 11g status: running ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode OPEN privacy OFF txpower 21.5 scanvalid 60 protmode CTS wme burst dtimperiod 1 -dfs .... Параметр `hostap` указывает, что интерфейс работает в режиме точки доступа на основе хоста. Настройка интерфейса может быть выполнена автоматически при загрузке, если добавить следующие строки в [.filename]#/etc/rc.conf#: [.programlisting] .... wlans_ath0="wlan0" create_args_wlan0="wlanmode hostap" ifconfig_wlan0="inet 192.168.0.1 netmask 255.255.255.0 ssid freebsdap mode 11g channel 1" .... ==== Точка доступа на основе хоста без аутентификации или шифрования Хотя не рекомендуется запускать точку доступа без какой-либо аутентификации или шифрования, это простой способ проверить, работает ли точка доступа. Такая конфигурация также важна для отладки проблем с клиентами. После настройки точки доступа выполните сканирование с другого беспроводного устройства для её обнаружения: [source, shell] .... # ifconfig wlan0 create wlandev ath0 # ifconfig wlan0 up scan SSID/MESH ID BSSID CHAN RATE S:N INT CAPS freebsdap 00:11:95:c3:0d:ac 1 54M -66:-96 100 ES WME .... Клиентская машина обнаружила точку доступа и может быть ассоциирована с ней: [source, shell] .... # ifconfig wlan0 inet 192.168.0.2 netmask 255.255.255.0 ssid freebsdap # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether 00:11:95:d5:43:62 inet 192.168.0.2 netmask 0xffffff00 broadcast 192.168.0.255 media: IEEE 802.11 Wireless Ethernet OFDM/54Mbps mode 11g status: associated ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac country US ecm authmode OPEN privacy OFF txpower 21.5 bmiss 7 scanvalid 60 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme burst .... [[network-wireless-ap-wpa]] ==== Точка доступа WPA2 на основе хоста Этот раздел посвящён настройке точки доступа на хосте FreeBSD с использованием протокола безопасности WPA2. Подробнее о WPA и настройке беспроводных клиентов на основе WPA можно узнать в crossref:advanced-networking[network-wireless-wpa-eap-tls, WPA с EAP-TLS]. Демон man:hostapd[8] используется для обработки аутентификации клиентов и управления ключами на точке доступа с поддержкой WPA2. В следующих операциях конфигурации выполняются на машине FreeBSD, выступающей в качестве точки доступа (AP). После того как точка доступа работает корректно, man:hostapd[8] можно автоматически запускать при загрузке, добавив эту строку в [.filename]#/etc/rc.conf#: [.programlisting] .... hostapd_enable="YES" .... Прежде чем пытаться настроить man:hostapd[8], сначала настройте основные параметры, описанные в crossref:advanced-networking[network-wireless-ap-basic, Основные настройки]. ===== WPA2-PSK WPA2-PSK предназначен для небольших сетей, где использование сервера аутентификации невозможно или нежелательно. Конфигурация выполняется в [.filename]#/etc/hostapd.conf#: [.programlisting] .... interface=wlan0 <.> debug=1 <.> ctrl_interface=/var/run/hostapd <.> ctrl_interface_group=wheel <.> ssid=freebsdap <.> wpa=2 <.> wpa_passphrase=freebsdmall <.> wpa_key_mgmt=WPA-PSK <.> wpa_pairwise=CCMP <.> .... <.> Беспроводной интерфейс, используемый для точки доступа. <.> Уровень детализации, используемый во время выполнения man:hostapd[8]. Значение `1` представляет минимальный уровень. <.> Путь к каталогу, используемому man:hostapd[8] для хранения файлов доменных сокетов для взаимодействия с внешними программами, такими как man:hostapd_cli[8]. В этом примере используется значение по умолчанию. <.> Группа, которой разрешён доступ к файлам управляющего интерфейса. <.> Имя беспроводной сети, или SSID, которое будет отображаться при сканировании беспроводных сетей. <.> Включает WPA и указывает, какой протокол аутентификации WPA будет использоваться. Значение `2` настраивает точку доступа на WPA2 и является рекомендуемым. Установите значение `1` только если требуется устаревший WPA. <.> ASCII-пароль для аутентификации WPA. <.> Протокол управления ключами для использования. В этом примере установлен WPA-PSK. <.> Алгоритмы шифрования, принимаемые точкой доступа. В этом примере принимается только шифр CCMP (AES). CCMP является альтернативой TKIP и настоятельно рекомендуется к использованию, когда это возможно. TKIP следует разрешать только в случае наличия станций, не способных использовать CCMP. Следующий шаг — запустить man:hostapd[8]: [source, shell] .... # service hostapd forcestart .... [source, shell] .... # ifconfig wlan0 wlan0: flags=8943 metric 0 mtu 1500 ether 04:f0:21:16:8e:10 inet6 fe80::6f0:21ff:fe16:8e10%wlan0 prefixlen 64 scopeid 0x9 nd6 options=21 media: IEEE 802.11 Wireless Ethernet autoselect mode 11na status: running ssid No5ignal channel 36 (5180 MHz 11a ht/40+) bssid 04:f0:21:16:8e:10 country US ecm authmode WPA2/802.11i privacy MIXED deftxkey 2 AES-CCM 2:128-bit AES-CCM 3:128-bit txpower 17 mcastrate 6 mgmtrate 6 scanvalid 60 ampdulimit 64k ampdudensity 8 shortgi wme burst dtimperiod 1 -dfs groups: wlan .... После запуска точки доступа клиенты могут подключиться к ней. Подробнее см. в разделе crossref:advanced-networking[network-wireless-ap-basic, Основные настройки]. Список станций, подключённых к точке доступа, можно просмотреть с помощью команды `ifconfig _wlan0_ list sta`. [[network-usb-tethering]] == Раздача интернета через USB Многие мобильные телефоны предоставляют возможность совместного использования своего интернет-подключения через USB (часто называемую "тетеринг, раздача Интернета или режим модема"). Эта функция использует один из протоколов: RNDIS, CDC или проприетарный протокол Apple(R) iPhone(R)/iPad(R). * Устройства Android(TM) обычно используют драйвер man:urndis[4]. * Устройства Apple(R) используют драйвер man:ipheth[4]. * Старые устройства часто используют драйвер man:cdce[4]. Перед подключением устройства загрузите соответствующий драйвер в ядро: [source, shell] .... # kldload if_urndis # kldload if_cdce # kldload if_ipheth .... После подключения устройства ``ue``_0_ будет доступен для использования как обычное сетевое устройство. Убедитесь, что на устройстве включена опция "USB-тетеринг". Чтобы сделать это изменение постоянным и загружать драйвер как модуль при загрузке, добавьте соответствующую строку из следующих в [.filename]#/boot/loader.conf#: [source, shell] .... if_urndis_load="YES" if_cdce_load="YES" if_ipheth_load="YES" .... [[network-bluetooth]] == Bluetooth Bluetooth — это беспроводная технология для создания персональных сетей, работающих в нелицензируемом диапазоне 2.4 ГГц, с радиусом действия до 10 метров. Сети обычно формируются на лету из портативных устройств, таких как мобильные телефоны, карманные компьютеры и ноутбуки. В отличие от технологии Wi-Fi, Bluetooth предоставляет сервисы более высокого уровня, такие как FTP-подобные файловые серверы, передача файлов, передача голоса, эмуляция последовательной линии и многое другое. Этот раздел описывает использование USB Bluetooth адаптера в системе FreeBSD. Затем рассматриваются различные протоколы и утилиты Bluetooth. === Загрузка поддержки Bluetooth Стек Bluetooth в FreeBSD реализован с использованием фреймворка man:netgraph[4]. Широкий спектр Bluetooth USB-адаптеров поддерживается драйвером man:ng_ubt[4]. Устройства Bluetooth на базе Broadcom BCM2033 поддерживаются драйверами man:ubtbcmfw[4] и man:ng_ubt[4]. Карта Bluetooth PC Card 3CRWB60-A от 3Com поддерживается драйвером man:ng_bt3c[4]. Bluetooth-устройства на основе последовательного порта и UART поддерживаются драйвером man:ng_h4[4] и утилитой man:hcseriald[8]. Прежде чем подключить устройство, определите, какой из вышеуказанных драйверов оно использует, затем загрузите драйвер. Например, если устройство использует драйвер man:ng_ubt[4]: [source, shell] .... # kldload ng_ubt .... Если устройство Bluetooth будет подключено к системе во время её загрузки, можно настроить систему на автоматическую загрузку модуля, добавив драйвер в [.filename]#/boot/loader.conf#: [.programlisting] .... ng_ubt_load="YES" .... После загрузки драйвера подключите USB-адаптер. Если загрузка драйвера прошла успешно, на консоли и в [.filename]#/var/log/messages# появится вывод, похожий на следующий: [source, shell] .... ubt0: vendor 0x0a12 product 0x0001, rev 1.10/5.25, addr 2 ubt0: Interface 0 endpoints: interrupt=0x81, bulk-in=0x82, bulk-out=0x2 ubt0: Interface 1 (alt.config 5) endpoints: isoc-in=0x83, isoc-out=0x3, wMaxPacketSize=49, nframes=6, buffer size=294 .... Для запуска и остановки стека Bluetooth используйте его стартовый скрипт. Рекомендуется остановить стек перед отключением устройства. Запуск стека Bluetooth может потребовать запуска man:hcsecd[8]. При запуске стека вывод должен быть похож на следующий: [source, shell] .... # service bluetooth start ubt0 BD_ADDR: 00:02:72:00:d4:1a Features: 0xff 0xff 0xf 00 00 00 00 00 <3-Slot> <5-Slot> Max. ACL packet size: 192 bytes Number of ACL packets: 8 Max. SCO packet size: 64 bytes Number of SCO packets: 8 .... === Поиск других устройств Bluetooth Интерфейс Host Controller Interface (HCI) предоставляет единый метод доступа к базовым возможностям Bluetooth. В FreeBSD узел netgraph HCI создаётся для каждого устройства Bluetooth. Подробнее см. man:ng_hci[4]. Одной из наиболее распространённых задач является обнаружение Bluetooth-устройств в радиусе действия. Эта операция называется _сканирование_ (inquiry). Запрос и другие операции, связанные с HCI, выполняются с помощью man:hccontrol[8]. В приведённом ниже примере показано, как выяснить, какие Bluetooth-устройства находятся в зоне действия. Список устройств должен отобразиться через несколько секунд. Обратите внимание, что удалённое устройство ответит на запрос только в том случае, если оно находится в режиме _обнаруживаемое_ (discoverable). [source, shell] .... % hccontrol -n ubt0hci inquiry Inquiry result, num_responses=1 Inquiry result #0 BD_ADDR: 00:80:37:29:19:a4 Page Scan Rep. Mode: 0x1 Page Scan Period Mode: 00 Page Scan Mode: 00 Class: 52:02:04 Clock offset: 0x78ef Inquiry complete. Status: No error [00] .... `BD_ADDR` — это уникальный адрес Bluetooth-устройства, аналогичный MAC-адресу сетевой карты. Этот адрес необходим для дальнейшего взаимодействия с устройством, и ему можно присвоить удобочитаемое имя. Информация об известных Bluetooth-хостах содержится в файле [.filename]#/etc/bluetooth/hosts#. В следующем примере показано, как получить удобочитаемое имя, присвоенное удалённому устройству: [source, shell] .... % hccontrol -n ubt0hci remote_name_request 00:80:37:29:19:a4 BD_ADDR: 00:80:37:29:19:a4 Name: Pav's T39 .... Если выполняется запрос к удалённому устройству Bluetooth, компьютер будет обнаружен как "your.host.name (ubt0)". Имя, назначенное локальному устройству, можно изменить в любое время. Удалённым устройствам могут быть назначены псевдонимы в [.filename]#/etc/bluetooth/hosts#. Дополнительная информация о файле [.filename]#/etc/bluetooth/hosts# может быть найдена в man:bluetooth.hosts[5]. Система Bluetooth обеспечивает соединение точка-точка между двумя устройствами Bluetooth или соединение точка-многоточка, разделяемое между несколькими устройствами Bluetooth. В следующем примере показано, как создать соединение с удалённым устройством: [source, shell] .... % hccontrol -n ubt0hci create_connection BT_ADDR .... `create_connection` принимает `BT_ADDR`, а также псевдонимы хостов в файле [.filename]#/etc/bluetooth/hosts#. Следующий пример показывает, как получить список активных базовых соединений для локального устройства: [source, shell] .... % hccontrol -n ubt0hci read_connection_list Remote BD_ADDR Handle Type Mode Role Encrypt Pending Queue State 00:80:37:29:19:a4 41 ACL 0 MAST NONE 0 0 OPEN .... _Дескриптор соединения (connection handle)_ полезен, когда требуется разрыв базового соединения, хотя обычно это не нужно делать вручную. Стек автоматически разрывает неактивные базовые соединения. [source, shell] .... # hccontrol -n ubt0hci disconnect 41 Connection handle: 41 Reason: Connection terminated by local host [0x16] .... Введите `hccontrol help` для получения полного списка доступных команд HCI. Большинство команд HCI не требуют прав суперпользователя. === Сопряжение устройств По умолчанию Bluetooth-связь не требует аутентификации, и любое устройство может взаимодействовать с любым другим устройством. Устройство Bluetooth, такое как сотовый телефон, может потребовать аутентификацию для предоставления определённой услуги. Аутентификация Bluetooth обычно выполняется с помощью _PIN-кода_ — строки ASCII длиной до 16 символов. Пользователь должен ввести один и тот же PIN-код на обоих устройствах. После ввода PIN-кода оба устройства сгенерируют _ключ связи_. Затем ключ связи может быть сохранен либо в самих устройствах, либо в постоянном хранилище. В следующий раз оба устройства будут использовать ранее сгенерированный ключ связи. Эта процедура называется _сопряжением (pairing)_. Обратите внимание, что если ключ связи будет утерян одним из устройств, спаривание необходимо повторить. Демон man:hcsecd[8] отвечает за обработку запросов аутентификации Bluetooth. Конфигурационный файл по умолчанию — [.filename]#/etc/bluetooth/hcsecd.conf#. Пример раздела для мобильного телефона с PIN-кодом `1234` приведён ниже: [.programlisting] .... device { bdaddr 00:80:37:29:19:a4; name "Pav's T39"; key nokey; pin "1234"; } .... Единственное ограничение PIN-кодов — их длина. Некоторые устройства, например Bluetooth-гарнитуры, могут иметь встроенный фиксированный PIN-код. Ключ `-d` заставляет man:hcsecd[8] оставаться на переднем плане, что упрощает отслеживание происходящего. Настройте удалённое устройство на приём сопряжения и инициируйте Bluetooth-соединение с ним. Удалённое устройство должно подтвердить принятие сопряжения и запросить PIN-код. Введите тот же PIN-код, который указан в [.filename]#hcsecd.conf#. Теперь компьютер и удалённое устройство сопряжены. Также сопряжение можно инициировать с удалённого устройства. Следующую строку можно добавить в [.filename]#/etc/rc.conf#, чтобы настроить автоматический запуск man:hcsecd[8] при загрузке системы: [.programlisting] .... hcsecd_enable="YES" .... Вот пример вывода демона man:hcsecd[8]: [.programlisting] .... hcsecd[16484]: Got Link_Key_Request event from 'ubt0hci', remote bdaddr 0:80:37:29:19:a4 hcsecd[16484]: Found matching entry, remote bdaddr 0:80:37:29:19:a4, name 'Pav's T39', link key doesn't exist hcsecd[16484]: Sending Link_Key_Negative_Reply to 'ubt0hci' for remote bdaddr 0:80:37:29:19:a4 hcsecd[16484]: Got PIN_Code_Request event from 'ubt0hci', remote bdaddr 0:80:37:29:19:a4 hcsecd[16484]: Found matching entry, remote bdaddr 0:80:37:29:19:a4, name 'Pav's T39', PIN code exists hcsecd[16484]: Sending PIN_Code_Reply to 'ubt0hci' for remote bdaddr 0:80:37:29:19:a4 .... === Доступ в сеть с профилями PPP Профиль Dial-Up Networking (DUN) может использоваться для настройки сотового телефона в качестве беспроводного модема для подключения к серверу доступа в Интернет через коммутируемое соединение. Он также может применяться для настройки компьютера для приёма входящих вызовов передачи данных с сотового телефона. Доступ к сети с профилем PPP может использоваться для предоставления доступа к LAN для одного устройства Bluetooth или нескольких устройств Bluetooth. Также он может обеспечить соединение между компьютерами с использованием PPP-сетей через эмуляцию последовательного кабеля. В FreeBSD эти профили реализованы с помощью man:ppp[8] и обёртки man:rfcomm_pppd[8], которая преобразует Bluetooth-соединение в форму, пригодную для использования PPP. Перед использованием профиля необходимо создать новую метку PPP в [.filename]#/etc/ppp/ppp.conf#. Примеры можно найти в man:rfcomm_pppd[8]. В этом примере man:rfcomm_pppd[8] используется для открытия соединения с удалённым устройством с `BD_ADDR` `00:80:37:29:19:a4` на DUNRFCOMM канале: [source, shell] .... # rfcomm_pppd -a 00:80:37:29:19:a4 -c -C dun -l rfcomm-dialup .... Фактический номер канала будет получен с удалённого устройства с использованием протокола SDP. Можно указать канал RFCOMM вручную, и в этом случае man:rfcomm_pppd[8] не будет выполнять запрос SDP. Используйте man:sdpcontrol[8], чтобы узнать канал RFCOMM на удалённом устройстве. Для предоставления сетевого доступа через службу PPPLAN необходимо, чтобы работал man:sdpd[8], и была создана новая запись для клиентов LAN в файле [.filename]#/etc/ppp/ppp.conf#. Примеры можно найти в man:rfcomm_pppd[8]. Наконец, запустите сервер RFCOMMPPP на допустимом номере канала RFCOMM. Сервер RFCOMMPPP автоматически зарегистрирует службу Bluetooth LAN в локальном демоне SDP. В приведённом ниже примере показано, как запустить сервер RFCOMMPPP. [source, shell] .... # rfcomm_pppd -s -C 7 -l rfcomm-server .... === Протоколы Bluetooth Этот раздел предоставляет обзор различных протоколов Bluetooth, их функций и связанных с ними утилит. ==== Logical Link Control and Adaptation Protocol (L2CAP) Протокол управления логическим соединением и адаптации (L2CAP) предоставляет сервисы передачи данных с установлением соединения и без него для протоколов верхнего уровня. L2CAP позволяет протоколам более высокого уровня и приложениям передавать и принимать пакеты данных L2CAP размером до 64 килобайт. L2CAP основан на концепции _каналов_. Канал — это логическое соединение поверх базового соединения, где каждый канал связан с одним протоколом по принципу "многие к одному". Несколько каналов могут быть связаны с одним и тем же протоколом, но канал не может быть связан с несколькими протоколами. Каждый полученный L2CAP-пакет на канале направляется соответствующему протоколу более высокого уровня. Несколько каналов могут совместно использовать одно и то же базовое соединение. В FreeBSD для каждого устройства Bluetooth создаётся узел netgraph типа L2CAP. Этот узел обычно соединен с нижестоящим узлом Bluetooth HCI и вышестоящими узлами Bluetooth-сокет. По умолчанию узел L2CAP имеет имя "devicel2cap". Для получения дополнительной информации обратитесь к man:ng_l2cap[4]. Полезной командой является man:l2ping[8], которую можно использовать для проверки связи с другими устройствами. Некоторые реализации Bluetooth могут не возвращать все отправленные им данные, поэтому `0 байт` в следующем примере является нормой. [source, shell] .... # l2ping -a 00:80:37:29:19:a4 0 bytes from 0:80:37:29:19:a4 seq_no=0 time=48.633 ms result=0 0 bytes from 0:80:37:29:19:a4 seq_no=1 time=37.551 ms result=0 0 bytes from 0:80:37:29:19:a4 seq_no=2 time=28.324 ms result=0 0 bytes from 0:80:37:29:19:a4 seq_no=3 time=46.150 ms result=0 .... Утилита man:l2control[8] используется для выполнения различных операций с узлами L2CAP. Этот пример показывает, как получить список логических соединений (каналов) и список базовых соединений для локального устройства: [source, shell] .... % l2control -a 00:02:72:00:d4:1a read_channel_list L2CAP channels: Remote BD_ADDR SCID/ DCID PSM IMTU/ OMTU State 00:07:e0:00:0b:ca 66/ 64 3 132/ 672 OPEN % l2control -a 00:02:72:00:d4:1a read_connection_list L2CAP connections: Remote BD_ADDR Handle Flags Pending State 00:07:e0:00:0b:ca 41 O 0 OPEN .... Еще один инструмент диагностики — man:btsockstat[1]. Он похож на man:netstat[1], но предназначен для структур данных, связанных с Bluetooth-сетями. В примере ниже показано то же логическое соединение, что и в man:l2control[8] выше. [source, shell] .... % btsockstat Active L2CAP sockets PCB Recv-Q Send-Q Local address/PSM Foreign address CID State c2afe900 0 0 00:02:72:00:d4:1a/3 00:07:e0:00:0b:ca 66 OPEN Active RFCOMM sessions L2PCB PCB Flag MTU Out-Q DLCs State c2afe900 c2b53380 1 127 0 Yes OPEN Active RFCOMM sockets PCB Recv-Q Send-Q Local address Foreign address Chan DLCI State c2e8bc80 0 250 00:02:72:00:d4:1a 00:07:e0:00:0b:ca 3 6 OPEN .... ==== Радиочастотная связь (RFCOMM) Протокол RFCOMM обеспечивает эмуляцию последовательных портов поверх протокола L2CAP. RFCOMM — это простой транспортный протокол с дополнительными возможностями для эмуляции 9 последовательных портов RS-232 (EIATIA-232-E). Он поддерживает до 60 одновременных соединений (каналов RFCOMM) между двумя устройствами Bluetooth. Для целей RFCOMM полный путь передачи данных включает два приложения, работающие на конечных точках соединения, и сегмент передачи данных между ними. RFCOMM предназначен для приложений, использующих последовательные порты устройств, в которых они работают. Сегмент передачи данных представляет собой прямое Bluetooth-соединение между устройствами. RFCOMM занимается только соединением между устройствами в случае прямого подключения или между устройством и модемом в случае сетевого подключения. RFCOMM может поддерживать другие конфигурации, такие как модули, которые обмениваются данными через технологию беспроводной связи Bluetooth с одной стороны и предоставляют проводной интерфейс с другой стороны. В FreeBSD RFCOMM реализован на уровне сокетов Bluetooth. ==== Протокол обнаружения служб (SDP) Протокол обнаружения служб (SDP) предоставляет клиентским приложениям возможность обнаруживать существование служб, предоставляемых серверными приложениями, а также атрибуты этих служб. Атрибуты службы включают тип или класс предоставляемой службы, а также информацию о механизме или протоколе, необходимую для использования службы. SDP включает взаимодействие между сервером SDP и клиентом SDP. Сервер хранит список записей служб, которые описывают характеристики служб, связанных с сервером. Каждая запись службы содержит информацию об отдельной службе. Клиент может получить информацию из записи службы, хранящейся на сервере SDP, отправив запрос SDP. Если клиент или приложение, связанное с клиентом, решает использовать службу, он должен установить отдельное соединение с провайдером службы для её использования. SDP предоставляет механизм для обнаружения служб и их атрибутов, но не предоставляет механизма для использования этих служб. Обычно клиент SDP ищет услуги на основе определённых желаемых характеристик. Однако бывают случаи, когда необходимо обнаружить, какие типы услуг описаны в записях сервера SDP без какой-либо предварительной информации об этих услугах. Этот процесс поиска любых предлагаемых услуг называется _обзором (browsing)_. Сервер Bluetooth SDP, man:sdpd[8], и клиент командной строки — man:sdpcontrol[8], включены в стандартную установку FreeBSD. В следующем примере показано, как выполнить запрос обзора SDP. [source, shell] .... % sdpcontrol -a 00:01:03:fc:6e:ec browse Record Handle: 00000000 Service Class ID List: Service Discovery Server (0x1000) Protocol Descriptor List: L2CAP (0x0100) Protocol specific parameter #1: u/int/uuid16 1 Protocol specific parameter #2: u/int/uuid16 1 Record Handle: 0x00000001 Service Class ID List: Browse Group Descriptor (0x1001) Record Handle: 0x00000002 Service Class ID List: LAN Access Using PPP (0x1102) Protocol Descriptor List: L2CAP (0x0100) RFCOMM (0x0003) Protocol specific parameter #1: u/int8/bool 1 Bluetooth Profile Descriptor List: LAN Access Using PPP (0x1102) ver. 1.0 .... Обратите внимание, что каждая служба имеет список атрибутов, таких как канал RFCOMM. В зависимости от службы пользователю может потребоваться запомнить некоторые из атрибутов. Некоторые реализации Bluetooth не поддерживают обзор служб и могут возвращать пустой список. В этом случае можно выполнить поиск конкретной службы. В примере ниже показано, как выполнить поиск службы OBEX Object Push (OPUSH): [source, shell] .... % sdpcontrol -a 00:01:03:fc:6e:ec search OPUSH .... Предоставление услуг на FreeBSD клиентам Bluetooth осуществляется с помощью сервера man:sdpd[8]. Следующую строку можно добавить в [.filename]#/etc/rc.conf#: [.programlisting] .... sdpd_enable="YES" .... Затем демон man:sdpd[8] можно запустить с помощью: [source, shell] .... # service sdpd start .... Локальное серверное приложение, которое хочет предоставить сервис Bluetooth удалённым клиентам, зарегистрирует сервис в локальном демоне SDP. Примером такого приложения является man:rfcomm_pppd[8]. После запуска оно зарегистрирует сервис Bluetooth LAN в локальном демоне SDP. Список служб, зарегистрированных на локальном сервере SDP, можно получить, выполнив запрос обзора SDP через локальный управляющий канал: [source, shell] .... # sdpcontrol -l browse .... ==== Отправка объектов протоколом OBEX (Object Push — OPUSH) Обмен объектами (OBEX) — это широко используемый протокол для простой передачи файлов между мобильными устройствами. Основное применение он находит в инфракрасной связи, где используется для передачи файлов общего назначения между ноутбуками или КПК, а также для отправки визитных карточек или записей календаря между сотовыми телефонами и другими устройствами с приложениями для управления персональной информацией (PIM). Сервер и клиент OBEX реализованы в obexapp, который можно установить с помощью package:comms/obexapp[] или порта. Клиент OBEX используется для отправки и/или получения объектов с сервера OBEX. Пример объекта — визитная карточка или встреча. Клиент OBEX может получить номер канала RFCOMM от удалённого устройства через SDP. Это можно сделать, указав имя службы вместо номера канала RFCOMM. Поддерживаемые имена служб: `IrMC`, `FTRN` и `OPUSH`. Также можно указать канал RFCOMM в виде числа. Ниже приведён пример сеанса OBEX, в котором объект информации об устройстве получается с мобильного телефона, а новый объект, визитная карточка, отправляется в директорию телефона. [source, shell] .... % obexapp -a 00:80:37:29:19:a4 -C IrMC obex> get telecom/devinfo.txt devinfo-t39.txt Success, response: OK, Success (0x20) obex> put new.vcf Success, response: OK, Success (0x20) obex> di Success, response: OK, Success (0x20) .... Для предоставления службы OPUSH должен быть запущен man:sdpd[8], а также должна быть создана корневая папка, в которой будут храниться все входящие объекты. Путь к корневой папке по умолчанию — [.filename]#/var/spool/obex#. Наконец, запустите сервер OBEX на допустимом номере канала RFCOMM. Сервер OBEX автоматически зарегистрирует службу OPUSH в локальном демоне SDP. В приведённом ниже примере показано, как запустить сервер OBEX. [source, shell] .... # obexapp -s -C 10 .... ==== Профиль последовательного порта (Serial Port Profile — SPP) Профиль последовательного порта (SPP) позволяет устройствам Bluetooth эмулировать последовательное соединение. Этот профиль позволяет устаревшим приложениям использовать Bluetooth в качестве замены кабеля через абстракцию виртуального последовательного порта. В FreeBSD man:rfcomm_sppd[1] реализует SPP, а псевдо-tty используется как абстракция виртуального последовательного порта. В примере ниже показано, как подключиться к сервису последовательного порта удалённого устройства. RFCOMM-канал не обязательно указывать, так как man:rfcomm_sppd[1] может получить его с удалённого устройства через SDP. Чтобы переопределить это, укажите RFCOMM-канал в командной строке. [source, shell] .... # rfcomm_sppd -a 00:07:E0:00:0B:CA -t rfcomm_sppd[94692]: Starting on /dev/pts/6... /dev/pts/6 .... После подключения псевдотерминал можно использовать как последовательный порт: [source, shell] .... # cu -l /dev/pts/6 .... Псевдотерминал выводится в stdout и может быть прочитан обёрточными скриптами: [.programlisting] .... PTS=`rfcomm_sppd -a 00:07:E0:00:0B:CA -t` cu -l $PTS .... === Устранение неполадок По умолчанию, когда хост FreeBSD принимает новое соединение, он пытается выполнить смену роли и стать ведущим. Некоторые старые устройства Bluetooth, которые не поддерживают смену роли, не смогут подключиться. Поскольку смена роли выполняется при установке нового соединения, невозможно запросить у удалённого устройства, поддерживает ли оно смену роли. Однако существует опция HCI для отключения смены роли на локальной стороне: [source, shell] .... # hccontrol -n ubt0hci write_node_role_switch 0 .... Для отображения пакетов Bluetooth используйте сторонний пакет hcidump, который можно установить с помощью package:comms/hcidump[] или порта. Эта утилита аналогична man:tcpdump[1] и может использоваться для вывода содержимого пакетов Bluetooth в терминал и для сохранения этих пакетов в файл. [[network-bridging]] == Создание моста Иногда полезно разделить сеть, например, сегмент Ethernet, на части без необходимости создания IP-подсетей и использования маршрутизатора для соединения сегментов. Устройство, которое так соединяет две сети, называется "мостом". Мост работает, изучая MAC-адреса устройств на каждом из своих сетевых интерфейсов. Он передаёт трафик между сетями только в том случае, если исходный и целевой MAC-адреса находятся в разных сетях. Во многих отношениях мост похож на Ethernet-коммутатор с очень малым количеством портов. Система FreeBSD с несколькими сетевыми интерфейсами может быть настроена как мост. Мост может быть полезен в следующих ситуациях: Соединение сетей:: Основная функция моста — объединить два или более сетевых сегмента. Существует множество причин использовать мост на основе хоста вместо сетевого оборудования, таких как ограничения по кабелям или межсетевой экран. Мост также может соединить беспроводной интерфейс, работающий в режиме hostap, с проводной сетью и выступать в качестве точки доступа. Межсетевой экран с фильтрацией и управлением трафиком:: Мост может использоваться, когда требуется функциональность межсетевого экрана без маршрутизации или преобразования сетевых адресов (NAT). + Пример — небольшая компания, подключённая через DSL или ISDN к провайдеру. У неё есть тринадцать публичных IP-адресов от провайдера и десять компьютеров в сети. В этой ситуации использование маршрутизатора с межсетевым экраном затруднено из-за проблем с подсетями. Межсетевой экран на основе моста можно настроить без каких-либо проблем с IP-адресацией. Ответвитель сетевого трафика (Network Tap):: Мост может объединить два сетевых сегмента для проверки всех Ethernet-кадров, проходящих между ними, с использованием man:bpf[4] и man:tcpdump[1] на интерфейсе моста, или путем отправки копии всех кадров на дополнительный интерфейс, известный как span-порт. VPN уровня 2:: Две Ethernet-сети могут быть соединены через IP-канал путем моста между сетями через туннель EtherIP или решение на основе man:tap[4], такое как OpenVPN. Избыточность уровня 2:: Сеть может быть соединена несколькими каналами и использовать протокол Spanning Tree Protocol (STP) для блокировки избыточных путей. В этом разделе описывается, как настроить систему FreeBSD в качестве моста с использованием man:if_bridge[4]. Также доступен драйвер моста на основе netgraph, который описан в man:ng_bridge[4]. [NOTE] ==== Фильтрация пакетов может использоваться с любым пакетом межсетевого экрана, который интегрируется в фреймворк man:pfil[9]. Мост может использоваться как шейпер (ограничитель) трафика с man:altq[4] или man:dummynet[4]. ==== === Включение моста В FreeBSD man:if_bridge[4] — это модуль ядра, который автоматически загружается с помощью man:ifconfig[8] при создании мостового интерфейса. Также можно включить поддержку моста в собственное ядро, добавив `device if_bridge` в конфигурационный файл собственного ядра. Мост создаётся с помощью клонирования интерфейса. Чтобы создать интерфейс моста: [source, shell] .... # ifconfig bridge create bridge0 # ifconfig bridge0 bridge0: flags=8802 metric 0 mtu 1500 ether 96:3d:4b:f1:79:7a id 00:00:00:00:00:00 priority 32768 hellotime 2 fwddelay 15 maxage 20 holdcnt 6 proto rstp maxaddr 100 timeout 1200 root id 00:00:00:00:00:00 priority 0 ifcost 0 port 0 .... При создании интерфейса моста ему автоматически назначается случайно сгенерированный Ethernet-адрес. Параметры `maxaddr` и `timeout` определяют, сколько MAC-адресов будет хранить межсетевой экран в своей таблице переадресации и через сколько секунд каждая запись будет удалена после последнего обнаружения. Остальные параметры управляют работой STP. Далее укажите, какие сетевые интерфейсы добавить в качестве членов моста. Чтобы мост мог передавать пакеты, все интерфейсы-участники и сам мост должны быть включены: [source, shell] .... # ifconfig bridge0 addm fxp0 addm fxp1 up # ifconfig fxp0 up # ifconfig fxp1 up .... Мост теперь может передавать Ethernet-кадры между [.filename]#fxp0# и [.filename]#fxp1#. Добавьте следующие строки в [.filename]#/etc/rc.conf#, чтобы мост создавался при загрузке: [.programlisting] .... cloned_interfaces="bridge0" ifconfig_bridge0="addm fxp0 addm fxp1 up" ifconfig_fxp0="up" ifconfig_fxp1="up" .... Если мостовому хосту нужен IP-адрес, установите его на интерфейсе моста, а не на интерфейсах-участниках. Адрес можно задать статически или через DHCP. В этом примере задаётся статический IP-адрес: [source, shell] .... # ifconfig bridge0 inet 192.168.0.1/24 .... Также можно назначит IPv6 адрес интерфейсу моста. Чтобы изменения стали постоянными, добавьте информацию об адресации в [.filename]#/etc/rc.conf#. Этот пример использует динамический адрес IPv6: [.programlisting] .... ifconfig_bridge0_ipv6="inet6 auto_linklocal accept_rtadv" .... [NOTE] ==== Когда включена фильтрация пакетов, транзитные пакеты будут проходить через фильтр на входящем интерфейсе мостового интерфейса и исходящем на соответствующих интерфейсах. Любой из этапов может быть отключен. Если направление потока пакетов важно, лучше настроить межсетевой экран на интерфейсах-участниках, а не на самом мосте. Мост имеет несколько настраиваемых параметров для передачи не-IP и IP пакетов, а также межсетевой экран второго уровня с man:ipfw[8]. Подробнее см. man:if_bridge[4]. ==== === Включение протокола островного дерева (STP) Для правильной работы Ethernet-сети между двумя устройствами может существовать только один активный путь. Протокол STP обнаруживает петли и переводит избыточные соединения в заблокированное состояние. Если одно из активных соединений выйдет из строя, STP вычисляет новое дерево и активирует один из заблокированных путей, чтобы восстановить подключение ко всем точкам сети. Протокол Rapid Spanning Tree (RSTP или 802.1w) обеспечивает обратную совместимость с устаревшим STP. RSTP предоставляет более быструю сходимость и обменивается информацией с соседними коммутаторами для быстрого перехода в режим передачи без создания петель. FreeBSD поддерживает RSTP и STP в качестве режимов работы, причем RSTP является режимом по умолчанию. STP может быть включен на интерфейсах участников с помощью man:ifconfig[8]. Для моста с интерфейсами [.filename]#fxp0# и [.filename]#fxp1# включите STP командой: [source, shell] .... # ifconfig bridge0 stp fxp0 stp fxp1 bridge0: flags=8843 metric 0 mtu 1500 ether d6:cf:d5:a0:94:6d id 00:01:02:4b:d4:50 priority 32768 hellotime 2 fwddelay 15 maxage 20 holdcnt 6 proto rstp maxaddr 100 timeout 1200 root id 00:01:02:4b:d4:50 priority 32768 ifcost 0 port 0 member: fxp0 flags=1c7 port 3 priority 128 path cost 200000 proto rstp role designated state forwarding member: fxp1 flags=1c7 port 4 priority 128 path cost 200000 proto rstp role designated state forwarding .... Этот мост имеет идентификатор остовного дерева `00:01:02:4b:d4:50` и приоритет `32768`. Поскольку `root id` совпадает, это указывает на то, что данный мост является корневым для дерева. Еще один мост в сети также имеет включенный STP: [source, shell] .... bridge0: flags=8843 metric 0 mtu 1500 ether 96:3d:4b:f1:79:7a id 00:13:d4:9a:06:7a priority 32768 hellotime 2 fwddelay 15 maxage 20 holdcnt 6 proto rstp maxaddr 100 timeout 1200 root id 00:01:02:4b:d4:50 priority 32768 ifcost 400000 port 4 member: fxp0 flags=1c7 port 4 priority 128 path cost 200000 proto rstp role root state forwarding member: fxp1 flags=1c7 port 5 priority 128 path cost 200000 proto rstp role designated state forwarding .... Строка `root id 00:01:02:4b:d4:50 priority 32768 ifcost 400000 port 4` показывает, что корневым мостом является `00:01:02:4b:d4:50` с стоимостью пути `400000` от данного моста. Путь к корневому мосту проходит через `port 4`, которым является [.filename]#fxp0#. === Параметры интерфейса моста Несколько параметров `ifconfig` уникальны для мостовых интерфейсов. В этом разделе приведены некоторые общие варианты использования этих параметров. Полный список доступных параметров описан в man:ifconfig[8]. private:: Приватный интерфейс не передаёт трафик на другие порты, также обозначенные как приватные. Трафик блокируется безусловно, поэтому Ethernet-кадры, включая ARP-пакеты, не будут передаваться. Если требуется выборочная блокировка трафика, следует использовать межсетевой экран. span:: Порт зеркалирования (SPAN —Switch Port Analyzer) передаёт копию каждого Ethernet-фрейма, полученного мостом. Количество портов зеркалирования, настроенных на мосту, не ограничено, но если интерфейс назначен как порт зеркалирования, он не может использоваться в качестве обычного порта моста. Это наиболее полезно для пассивного мониторинга сети, подключенной к мосту, на другом хосте, подключенном к одному из портов зеркалирования моста. Например, чтобы отправить копию всех фреймов через интерфейс с именем [.filename]#fxp4#: + [source, shell] .... # ifconfig bridge0 span fxp4 .... sticky:: Если интерфейс участника моста помечен как фиксированный (sticky), динамически изученные записи адресов обрабатываются как статические записи в кэше пересылки. Фиксированные записи никогда не удаляются из кэша и не заменяются, даже если адрес обнаружен на другом интерфейсе. Это даёт преимущество статических записей адресов без необходимости предварительного заполнения таблицы пересылки. Клиенты, изученные на определённом сегменте моста, не могут перемещаться на другой сегмент. + Пример использования фиксированных адресов — это объединение моста с VLAN для изоляции сетей клиентов без потерь IP-адресного пространства. Предположим, что `CustomerA` находится в `vlan100`, `CustomerB` — в `vlan101`, а мост имеет адрес `192.168.0.1`: + [source, shell] .... # ifconfig bridge0 addm vlan100 sticky vlan100 addm vlan101 sticky vlan101 # ifconfig bridge0 inet 192.168.0.1/24 .... + В этом примере оба клиента видят `192.168.0.1` в качестве своего шлюза по умолчанию. Поскольку кэш моста устойчив, один узел не может подделать MAC-адрес другого клиента, чтобы перехватить его трафик. + Любое взаимодействие между VLAN может быть заблокировано с помощью межсетевого экрана или, как показано в этом примере, частных интерфейсов: + [source, shell] .... # ifconfig bridge0 private vlan100 private vlan101 .... + Клиенты полностью изолированы друг от друга, и весь диапазон адресов `/24` может быть выделен без разделения на подсети. + Количество уникальных исходных MAC-адресов за интерфейсом может быть ограничено. Как только лимит будет достигнут, пакеты с неизвестными исходными адресами будут отбрасываться до тех пор, пока существующая запись в кэше хоста не истечёт или не будет удалена. + Следующий пример устанавливает максимальное количество Ethernet-устройств для `CustomerA` на `vlan100` равным 10: + [source, shell] .... # ifconfig bridge0 ifmaxaddr vlan100 10 .... Мостовые интерфейсы также поддерживают режим мониторинга, в котором пакеты отбрасываются после обработки man:bpf[4] и не обрабатываются или передаются дальше. Это можно использовать для мультиплексирования входа двух или более интерфейсов в один поток man:bpf[4]. Это полезно для восстановления трафика сетевых кранов, которые передают сигналы RX/TX через два отдельных интерфейса. Например, чтобы читать входные данные с четырёх сетевых интерфейсов как один поток: [source, shell] .... # ifconfig bridge0 addm fxp0 addm fxp1 addm fxp2 addm fxp3 monitor up # tcpdump -i bridge0 .... === Мониторинг SNMP Интерфейс моста и параметры STP можно отслеживать с помощью man:bsnmpd[1], который включён в базовую систему FreeBSD. Экспортируемые MIB моста соответствуют стандартам IETF, поэтому для получения данных можно использовать любой SNMP-клиент или пакет мониторинга. Чтобы включить мониторинг на мосту, раскомментируйте эту строку в [.filename]#/etc/snmpd.config#, удалив символ `+#+` в начале: [.programlisting] .... begemotSnmpdModulePath."bridge" = "/usr/lib/snmp_bridge.so" .... Другие параметры конфигурации, такие как имена сообществ и списки доступа, возможно, потребуется изменить в этом файле. Подробнее см. в man:bsnmpd[1] и man:snmp_bridge[3]. После сохранения изменений добавьте следующую строку в [.filename]#/etc/rc.conf#: [.programlisting] .... bsnmpd_enable="YES" .... Затем, запустите man:bsnmpd[1]: [source, shell] .... # service bsnmpd start .... Следующие примеры используют программное обеспечение Net-SNMP (package:net-mgmt/net-snmp[]) для запроса моста с клиентской системы. Также можно использовать порт package:net-mgmt/bsnmptools[]. На SNMP-клиенте, где запущен Net-SNMP, добавьте следующие строки в [.filename]#$HOME/.snmp/snmp.conf#, чтобы импортировать определения MIB для моста: [.programlisting] .... mibdirs +/usr/share/snmp/mibs mibs +BRIDGE-MIB:RSTP-MIB:BEGEMOT-MIB:BEGEMOT-BRIDGE-MIB .... Для мониторинга одного моста с использованием IETF BRIDGE-MIB (RFC4188): [source, shell] .... % snmpwalk -v 2c -c public bridge1.example.com mib-2.dot1dBridge BRIDGE-MIB::dot1dBaseBridgeAddress.0 = STRING: 66:fb:9b:6e:5c:44 BRIDGE-MIB::dot1dBaseNumPorts.0 = INTEGER: 1 ports BRIDGE-MIB::dot1dStpTimeSinceTopologyChange.0 = Timeticks: (189959) 0:31:39.59 centi-seconds BRIDGE-MIB::dot1dStpTopChanges.0 = Counter32: 2 BRIDGE-MIB::dot1dStpDesignatedRoot.0 = Hex-STRING: 80 00 00 01 02 4B D4 50 ... BRIDGE-MIB::dot1dStpPortState.3 = INTEGER: forwarding(5) BRIDGE-MIB::dot1dStpPortEnable.3 = INTEGER: enabled(1) BRIDGE-MIB::dot1dStpPortPathCost.3 = INTEGER: 200000 BRIDGE-MIB::dot1dStpPortDesignatedRoot.3 = Hex-STRING: 80 00 00 01 02 4B D4 50 BRIDGE-MIB::dot1dStpPortDesignatedCost.3 = INTEGER: 0 BRIDGE-MIB::dot1dStpPortDesignatedBridge.3 = Hex-STRING: 80 00 00 01 02 4B D4 50 BRIDGE-MIB::dot1dStpPortDesignatedPort.3 = Hex-STRING: 03 80 BRIDGE-MIB::dot1dStpPortForwardTransitions.3 = Counter32: 1 RSTP-MIB::dot1dStpVersion.0 = INTEGER: rstp(2) .... Значение `dot1dStpTopChanges.0` равно двум, что указывает на двукратное изменение топологии STP-моста. Изменение топологии означает, что одна или несколько связей в сети изменились или вышли из строя, и было выполнено перерасчёт дерева. Значение `dot1dStpTimeSinceTopologyChange.0` покажет, когда это произошло. Для мониторинга нескольких интерфейсов моста можно использовать приватный BEGEMOT-BRIDGE-MIB: [source, shell] .... % snmpwalk -v 2c -c public bridge1.example.com enterprises.fokus.begemot.begemotBridge BEGEMOT-BRIDGE-MIB::begemotBridgeBaseName."bridge0" = STRING: bridge0 BEGEMOT-BRIDGE-MIB::begemotBridgeBaseName."bridge2" = STRING: bridge2 BEGEMOT-BRIDGE-MIB::begemotBridgeBaseAddress."bridge0" = STRING: e:ce:3b:5a:9e:13 BEGEMOT-BRIDGE-MIB::begemotBridgeBaseAddress."bridge2" = STRING: 12:5e:4d:74:d:fc BEGEMOT-BRIDGE-MIB::begemotBridgeBaseNumPorts."bridge0" = INTEGER: 1 BEGEMOT-BRIDGE-MIB::begemotBridgeBaseNumPorts."bridge2" = INTEGER: 1 ... BEGEMOT-BRIDGE-MIB::begemotBridgeStpTimeSinceTopologyChange."bridge0" = Timeticks: (116927) 0:19:29.27 centi-seconds BEGEMOT-BRIDGE-MIB::begemotBridgeStpTimeSinceTopologyChange."bridge2" = Timeticks: (82773) 0:13:47.73 centi-seconds BEGEMOT-BRIDGE-MIB::begemotBridgeStpTopChanges."bridge0" = Counter32: 1 BEGEMOT-BRIDGE-MIB::begemotBridgeStpTopChanges."bridge2" = Counter32: 1 BEGEMOT-BRIDGE-MIB::begemotBridgeStpDesignatedRoot."bridge0" = Hex-STRING: 80 00 00 40 95 30 5E 31 BEGEMOT-BRIDGE-MIB::begemotBridgeStpDesignatedRoot."bridge2" = Hex-STRING: 80 00 00 50 8B B8 C6 A9 .... Для изменения мониторинга интерфейса моста через поддерево `mib-2.dot1dBridge`: [source, shell] .... % snmpset -v 2c -c private bridge1.example.com BEGEMOT-BRIDGE-MIB::begemotBridgeDefaultBridgeIf.0 s bridge2 .... [[network-aggregation]] == Агрегация каналов и отказоустойчивость FreeBSD предоставляет интерфейс man:lagg[4], который может использоваться для объединения нескольких сетевых интерфейсов в один виртуальный интерфейс с целью обеспечения отказоустойчивости и агрегации каналов. Отказоустойчивость позволяет трафику продолжать передаваться, пока хотя бы один из объединённых сетевых интерфейсов имеет установленное соединение. Агрегация каналов наиболее эффективна на коммутаторах, поддерживающих LACP, так как этот протокол распределяет трафик в обоих направлениях, реагируя на отказ отдельных каналов. Протоколы агрегации, поддерживаемые интерфейсом lagg, определяют, какие порты используются для исходящего трафика и принимает ли конкретный порт входящий трафик. В man:lagg[4] поддерживаются следующие протоколы: failover:: Этот режим отправляет и получает трафик только через основной порт. Если основной порт становится недоступным, используется следующий активный порт. Первый интерфейс, добавленный в виртуальный интерфейс, является основным портом, а все последующие добавленные интерфейсы используются как устройства резервирования. Если происходит переключение на неосновной порт, исходный порт снова становится основным, как только становится доступным. loadbalance:: Это обеспечивает статическую настройку и не выполняет согласование агрегации с узлом или обмен кадрами для мониторинга связи. Если коммутатор поддерживает LACP, следует использовать его. lacp:: Протокол управления агрегацией каналов IEEE(R) 802.3ad (LACP) согласует набор агрегируемых каналов с узлом-партнёром в один или несколько агрегированных групп каналов (LAG). Каждая LAG состоит из портов с одинаковой скоростью, настроенных на полнодуплексный режим работы, а трафик распределяется между портами в LAG с наибольшей общей скоростью. Обычно существует только одна LAG, содержащая все порты. В случае изменений физической связности LACP быстро сходится к новой конфигурации. + LACP распределяет исходящий трафик по активным портам на основе хешированной информации заголовков протоколов и принимает входящий трафик с любого активного порта. Хеш включает исходный и целевой Ethernet-адреса, а также, если доступно, тег VLAN и исходный и целевой IPv4 или IPv6 адреса. roundrobin:: Этот режим распределяет исходящий трафик с помощью циклического планировщика через все активные порты и принимает входящий трафик с любого активного порта. Поскольку этот режим нарушает порядок следования Ethernet-кадров, его следует использовать с осторожностью. broadcast:: Этот режим отправляет исходящий трафик на все порты, настроенные на интерфейсе lagg, и принимает кадры на любом порту. === Примеры конфигурации В этом разделе показано, как настроить коммутатор Cisco(R) и систему FreeBSD для балансировки нагрузки LACP. Затем демонстрируется настройка двух Ethernet-интерфейсов в режиме отказоустойчивости, а также настройка режима отказоустойчивости между Ethernet и беспроводным интерфейсом. [[networking-lacp-aggregation-cisco]] .Агрегация каналов с использованием LACP и коммутатора Cisco(R) [example] ==== В этом примере два Ethernet-интерфейса man:fxp[4] на машине FreeBSD подключены к первым двум Ethernet-портам коммутатора Cisco(R) в виде единого отказоустойчивого канала с балансировкой нагрузки. Дополнительные интерфейсы могут быть добавлены для увеличения пропускной способности и отказоустойчивости. Замените названия портов Cisco(R), Ethernet-устройств, номер группы каналов и IP-адрес, указанные в примере, в соответствии с вашей конфигурацией. Порядок кадров обязателен на Ethernet-соединениях, и любой трафик между двумя станциями всегда проходит через одно и то же физическое соединение, что ограничивает максимальную скорость пропускной способностью одного интерфейса. Алгоритм передачи пытается использовать как можно больше информации для различения отдельных потоков трафика и балансировки этих потоков между доступными интерфейсами. На коммутаторе Cisco(R) добавьте интерфейсы _FastEthernet0/1_ и _FastEthernet0/2_ в группу каналов _1_: [source, shell] .... interface FastEthernet0/1 channel-group 1 mode active channel-protocol lacp ! interface FastEthernet0/2 channel-group 1 mode active channel-protocol lacp .... На системе FreeBSD создайте интерфейс man:lagg[4], используя физические интерфейсы _fxp0_ и _fxp1_, и поднимите интерфейсы с IP-адресом _10.0.0.3/24_: [source, shell] .... # ifconfig fxp0 up # ifconfig fxp1 up # ifconfig lagg0 create # ifconfig lagg0 up laggproto lacp laggport fxp0 laggport fxp1 10.0.0.3/24 .... Далее проверьте состояние виртуального интерфейса: [source, shell] .... # ifconfig lagg0 lagg0: flags=8843 metric 0 mtu 1500 options=8 ether 00:05:5d:71:8d:b8 inet 10.0.0.3 netmask 0xffffff00 broadcast 10.0.0.255 media: Ethernet autoselect status: active laggproto lacp laggport: fxp1 flags=1c laggport: fxp0 flags=1c .... Порты, помеченные как `ACTIVE`, являются частью LAG, который был согласован с удалённым коммутатором. Через эти активные порты будет передаваться и приниматься трафик. Добавьте `-v` к приведённой выше команде, чтобы просмотреть идентификаторы LAG. Чтобы проверить статус порта на коммутаторе Cisco(R): [source, shell] .... switch# show lacp neighbor Flags: S - Device is requesting Slow LACPDUs F - Device is requesting Fast LACPDUs A - Device is in Active mode P - Device is in Passive mode Channel group 1 neighbors Partner's information: LACP port Oper Port Port Port Flags Priority Dev ID Age Key Number State Fa0/1 SA 32768 0005.5d71.8db8 29s 0x146 0x3 0x3D Fa0/2 SA 32768 0005.5d71.8db8 29s 0x146 0x4 0x3D .... Для получения более подробной информации введите `show lacp neighbor detail`. Чтобы сохранить эту конфигурацию после перезагрузки, добавьте следующие записи в [.filename]#/etc/rc.conf# на системе FreeBSD: [.programlisting] .... ifconfig_fxp0="up" ifconfig_fxp1="up" cloned_interfaces="lagg0" ifconfig_lagg0="laggproto lacp laggport fxp0 laggport fxp1 10.0.0.3/24" .... ==== [[networking-lagg-failover]] .Режим отказоустойчивости [example] ==== Режим отказоустойчивости может быть использован для переключения на резервный интерфейс, если соединение на основном интерфейсе потеряно. Для настройки отказоустойчивости убедитесь, что физические интерфейсы активны, затем создайте интерфейс man:lagg[4]. В этом примере _fxp0_ — основной интерфейс, _fxp1_ — резервный интерфейс, а виртуальному интерфейсу назначен IP-адрес _10.0.0.15/24_: [source, shell] .... # ifconfig fxp0 up # ifconfig fxp1 up # ifconfig lagg0 create # ifconfig lagg0 up laggproto failover laggport fxp0 laggport fxp1 10.0.0.15/24 .... Виртуальный интерфейс должен выглядеть примерно так: [source, shell] .... # ifconfig lagg0 lagg0: flags=8843 metric 0 mtu 1500 options=8 ether 00:05:5d:71:8d:b8 inet 10.0.0.15 netmask 0xffffff00 broadcast 10.0.0.255 media: Ethernet autoselect status: active laggproto failover laggport: fxp1 flags=0<> laggport: fxp0 flags=5 .... Трафик будет передаваться и приниматься через интерфейс _fxp0_. Если соединение на _fxp0_ будет потеряно, активным станет интерфейс _fxp1_. Если соединение на основном интерфейсе восстановится, он снова станет активным. Чтобы сохранить эту конфигурацию после перезагрузки, добавьте следующие записи в [.filename]#/etc/rc.conf#: [.programlisting] .... ifconfig_fxp0="up" ifconfig_fxp1="up" cloned_interfaces="lagg0" ifconfig_lagg0="laggproto failover laggport fxp0 laggport fxp1 10.0.0.15/24" .... ==== [[networking-lagg-wired-and-wireless]] .Режим автоматического переключения между Ethernet и беспроводным интерфейсом [example] ==== Для пользователей ноутбуков обычно желательно настроить беспроводное устройство как вторичное, которое используется только при отсутствии Ethernet-подключения. С помощью man:lagg[4] можно настроить отказоустойчивость, отдавая предпочтение Ethernet-подключению как по соображениям производительности, так и безопасности, сохраняя при этом возможность передачи данных через беспроводное соединение. Это достигается путем замены MAC-адреса Ethernet-интерфейса на MAC-адрес беспроводного интерфейса. [NOTE] -**** +==== Теоретически, либо Ethernet, либо беспроводной MAC-адрес можно изменить, чтобы они были одинаковыми. Однако некоторые популярные беспроводные интерфейсы не поддерживают переопределение MAC-адреса. Поэтому мы рекомендуем переопределить Ethernet MAC-адрес для этой цели. -**** +==== [NOTE] -**** +==== Если драйвер для беспроводного интерфейса не загружен в `GENERIC` или собственном ядре, и компьютер работает под FreeBSD {rel121-current}, загрузите соответствующий [.filename]#.ko# в [.filename]#/boot/loader.conf#, добавив `*driver_load="YES"*` в этот файл и перезагрузив систему. Другой, более предпочтительный способ — загрузить драйвер в [.filename]#/etc/rc.conf#, добавив его в `kld_list` (подробности см. в man:rc.conf[5]) в этом файле и перезагрузив систему. Это необходимо, потому что в противном случае драйвер ещё не загружен к моменту настройки интерфейса man:lagg[4]. -**** +==== В этом примере Ethernet-интерфейс _re0_ является основным, а беспроводной интерфейс _wlan0_ — резервным. Интерфейс _wlan0_ был создан на основе физического беспроводного интерфейса _ath0_, а Ethernet-интерфейс будет настроен с MAC-адресом беспроводного интерфейса. Сначала включите беспроводной интерфейс (замените _FR_ на код вашей страны из 2 букв), но не задавайте IP-адрес. Замените _wlan0_ на имя беспроводного интерфейса вашей системы: [source, shell] .... # ifconfig wlan0 create wlandev ath0 country FR ssid my_router up .... Определите MAC-адрес беспроводного интерфейса, например, так: [source, shell] .... # ifconfig wlan0 wlan0: flags=8843 metric 0 mtu 1500 ether b8:ee:65:5b:32:59 groups: wlan ssid Bbox-A3BD2403 channel 6 (2437 MHz 11g ht/20) bssid 00:37:b7:56:4b:60 regdomain ETSI country FR indoor ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 2:128-bit txpower 30 bmiss 7 scanvalid 60 protmode CTS ampdulimit 64k ampdudensity 8 shortgi -stbctx stbcrx -ldpc wme burst roaming MANUAL media: IEEE 802.11 Wireless Ethernet MCS mode 11ng status: associated nd6 options=29 .... Строка `ether` будет содержать MAC-адрес указанного интерфейса. Теперь измените MAC-адрес интерфейса Ethernet, чтобы он совпадал: [source, shell] .... # ifconfig re0 ether b8:ee:65:5b:32:59 .... Убедитесь, что интерфейс _re0_ включен, затем создайте интерфейс man:lagg[4] с _re0_ в качестве основного с переключением на _wlan0_ в случае отказа: [source, shell] .... # ifconfig re0 up # ifconfig lagg0 create # ifconfig lagg0 up laggproto failover laggport re0 laggport wlan0 .... Виртуальный интерфейс должен выглядеть примерно так: [source, shell] .... # ifconfig lagg0 lagg0: flags=8843 metric 0 mtu 1500 options=8 ether b8:ee:65:5b:32:59 laggproto failover lagghash l2,l3,l4 laggport: re0 flags=5 laggport: wlan0 flags=0<> groups: lagg media: Ethernet autoselect status: active .... Затем запустите DHCP-клиент для получения IP-адреса: [source, shell] .... # dhclient lagg0 .... Чтобы сохранить эту конфигурацию после перезагрузки, добавьте следующие записи в [.filename]#/etc/rc.conf#: [.programlisting] .... ifconfig_re0="ether b8:ee:65:5b:32:59" wlans_ath0="wlan0" ifconfig_wlan0="WPA" create_args_wlan0="country FR" cloned_interfaces="lagg0" ifconfig_lagg0="up laggproto failover laggport re0 laggport wlan0 DHCP" .... ==== [[network-diskless]] == Запуск системы по сети (PXE) без использования локальных накопителей Встроенная среда исполнения Intel(R) Preboot eXecution Environment (PXE) позволяет операционной системе загружаться по сети. Например, система FreeBSD может загружаться по сети и работать без локального диска, используя файловые системы, смонтированные с NFS-сервера. Поддержка PXE обычно доступна в BIOS. Чтобы использовать PXE при запуске машины, выберите опцию `Загрузка по сети` в настройках BIOS или нажмите функциональную клавишу во время инициализации системы. Для предоставления файлов, необходимых для загрузки операционной системы по сети, настройка PXE также требует правильно настроенных серверов DHCP, TFTP и NFS, где: * Исходные параметры, такие как IP-адрес, имя загружаемого исполняемого файла и его расположение, имя сервера и корневой путь, получаются от сервера DHCP. * Файл загрузчика операционной системы загружается с использованием TFTP. * Файловые системы загружаются с использованием NFS. Когда компьютер загружается по PXE, он получает информацию через DHCP о том, где получить начальный загрузочный файл. После того как хост-компьютер получает эту информацию, он загружает загрузчик через TFTP и затем выполняет его. В FreeBSD загрузочный файл называется [.filename]#/boot/pxeboot#. После выполнения [.filename]#/boot/pxeboot# загружается ядро FreeBSD, и продолжается остальная последовательность загрузки FreeBSD, как описано в crossref:boot[boot,Процесс загрузки FreeBSD]. [NOTE] ==== Для загрузки по PXE на UEFI фактический файл загрузчика, который следует использовать, это [.filename]#/boot/loader.efi#. См. раздел ниже crossref:advanced-networking[_debugging_pxe_problems,Отладка проблем PXE] о том, как использовать [.filename]#/boot/loader.efi#. ==== Этот раздел описывает, как настроить эти службы в системе FreeBSD, чтобы другие системы могли загружаться по PXE в FreeBSD. Дополнительную информацию можно найти в man:diskless[8]. [CAUTION] ==== Как уже упоминалось, система, предоставляющая эти службы, является небезопасной. Она должна находиться в защищённой части сети и не доверяться другим хостам. ==== [[network-pxe-nfs]] === Настройка окружения PXE В этом разделе показаны шаги по настройке встроенных серверов NFS и TFTP. В следующем разделе будет продемонстрировано, как установить и настроить сервер DHCP. В данном примере каталог, который будет содержать файлы, используемые пользователями PXE, называется [.filename]#/b/tftpboot/FreeBSD/install#. Важно, чтобы этот каталог существовал и чтобы одно и то же имя каталога было указано как в [.filename]#/etc/inetd.conf#, так и в [.filename]#/usr/local/etc/dhcpd.conf#. [NOTE] ==== Примеры команд ниже предполагают использование оболочки man:sh[1]. Пользователям man:csh[1] и man:tcsh[1] потребуется запустить оболочку man:sh[1] или адаптировать команды к синтаксису man:csh[1]. ==== [.procedure] . Создайте корневой каталог, который будет содержать установку FreeBSD для монтирования по NFS: + [source, shell] .... # export NFSROOTDIR=/b/tftpboot/FreeBSD/install # mkdir -p ${NFSROOTDIR} .... . Включите сервер NFS, добавив следующую строку в [.filename]#/etc/rc.conf#: + [.programlisting] .... nfs_server_enable="YES" .... . Экспортируйте корневой каталог бездисковой системы через NFS, добавив следующее в [.filename]#/etc/exports#: + [.programlisting] .... /b -ro -alldirs -maproot=root .... . Запустите сервер NFS: + [source, shell] .... # service nfsd start .... . Включите man:inetd[8], добавив следующую строку в [.filename]#/etc/rc.conf#: + [.programlisting] .... inetd_enable="YES" .... . Раскомментируйте следующую строку в [.filename]#/etc/inetd.conf#, убедившись, что она не начинается с символа `+#+`: + [.programlisting] .... tftp dgram udp wait root /usr/libexec/tftpd tftpd blocksize 1468 -l -s /b/tftpboot .... + [NOTE] ==== Указанный размер блока tftp, например, 1468 байт, заменяет стандартный размер 512 байт. Некоторые версии PXE требуют TCP-версию TFTP. В этом случае раскомментируйте вторую строку `tftp`, содержащую `stream tcp`. ==== . Запустите man:inetd[8]: + [source, shell] .... # service inetd start .... . Установите базовую систему в [.filename]#${NFSROOTDIR}#, либо распаковав официальные архивы, либо пересобрав ядро и пользовательское окружение FreeBSD (подробные инструкции можно найти в crossref:cutting-edge[makeworld,“Обновление FreeBSD из исходного кода”], но не забудьте добавить `DESTDIR=_${NFSROOTDIR}_` при выполнении команд `make installkernel` и `make installworld`). . Проверьте, что TFTP-сервер работает и может загрузить загрузчик, который будет получен через PXE: + [source, shell] .... # tftp localhost tftp> get FreeBSD/install/boot/pxeboot Received 264951 bytes in 0.1 seconds .... . Отредактируйте файл [.filename]#${NFSROOTDIR}/etc/fstab# и создайте запись для монтирования корневой файловой системы через NFS: + [.programlisting] .... # Device Mountpoint FSType Options Dump Pass myhost.example.com:/b/tftpboot/FreeBSD/install / nfs ro 0 0 .... + Замените _myhost.example.com_ на имя хоста или IP-адрес сервера NFS. В этом примере корневая файловая система монтируется в режиме только для чтения, чтобы предотвратить возможное удаление содержимого корневой файловой системы клиентами NFS. . Установите пароль root в среде PXE для клиентских машин, загружающихся через PXE: + [source, shell] .... # chroot ${NFSROOTDIR} # passwd .... . При необходимости разрешите вход root по man:ssh[1] для клиентских машин, загружающихся через PXE, отредактировав [.filename]#${NFSROOTDIR}/etc/ssh/sshd_config# и включив `PermitRootLogin`. Эта опция описана в man:sshd_config[5]. . Выполните другие необходимые настройки PXE-окружения в [.filename]#${NFSROOTDIR}#. Эти настройки могут включать установку пакетов или редактирование файла паролей с помощью man:vipw[8]. При загрузке с корневого тома NFS, [.filename]#/etc/rc# определяет загрузку по NFS и запускает [.filename]#/etc/rc.initdiskless#. В этом случае [.filename]#/etc# и [.filename]#/var# должны быть файловыми системами в памяти, чтобы эти каталоги были доступны для записи, тогда как корневой каталог NFS доступен только для чтения: [source, shell] .... # chroot ${NFSROOTDIR} # mkdir -p conf/base # tar -c -v -f conf/base/etc.cpio.gz --format cpio --gzip etc # tar -c -v -f conf/base/var.cpio.gz --format cpio --gzip var .... При загрузке системы будут созданы и смонтированы файловые системы в памяти для [.filename]#/etc# и [.filename]#/var#, а содержимое файлов [.filename]#cpio.gz# будет скопировано в них. По умолчанию эти файловые системы имеют максимальный размер 5 мегабайт. Если архивы не помещаются, что обычно происходит с [.filename]#/var# при установке бинарных пакетов, укажите больший размер, записав количество необходимых секторов по 512 байт (например, 5 мегабайт — это 10240 секторов) в файлы [.filename]#${NFSROOTDIR}/conf/base/etc/md_size# и [.filename]#${NFSROOTDIR}/conf/base/var/md_size# для файловых систем [.filename]#/etc# и [.filename]#/var# соответственно. [[network-pxe-setting-up-dhcp]] === Настройка DHCP-сервера Сервер DHCP не обязательно должен быть тем же самым компьютером, что и серверы TFTP и NFS, но он должен быть доступен в сети. DHCP не является частью базовой системы FreeBSD, но может быть установлен с помощью порта package:net/isc-dhcp44-server[] или пакета. После установки отредактируйте файл конфигурации [.filename]#/usr/local/etc/dhcpd.conf#. Настройте параметры `next-server`, `filename` и `root-path`, как показано в этом примере: [.programlisting] .... subnet 192.168.0.0 netmask 255.255.255.0 { range 192.168.0.2 192.168.0.3 ; option subnet-mask 255.255.255.0 ; option routers 192.168.0.1 ; option broadcast-address 192.168.0.255 ; option domain-name-servers 192.168.35.35, 192.168.35.36 ; option domain-name "example.com"; # IP address of TFTP server next-server 192.168.0.1 ; # path of boot loader obtained via tftp filename "FreeBSD/install/boot/pxeboot" ; # pxeboot boot loader will try to NFS mount this directory for root FS option root-path "192.168.0.1:/b/tftpboot/FreeBSD/install/" ; } .... Директива `next-server` используется для указания IP-адреса TFTP-сервера. Директива `filename` определяет путь к [.filename]#/boot/pxeboot#. Используется относительное имя файла, что означает, что [.filename]#/b/tftpboot# не включен в путь. Опция `root-path` определяет путь к корневой файловой системе NFS. После сохранения изменений включите DHCP при загрузке, добавив следующую строку в [.filename]#/etc/rc.conf#: [.programlisting] .... dhcpd_enable="YES" .... Затем запустите службу DHCP: [source, shell] .... # service isc-dhcpd start .... === Отладка проблем PXE После настройки и запуска всех служб клиенты PXE должны автоматически загружать FreeBSD по сети. Если конкретный клиент не может подключиться, при загрузке этой машины войдите в меню конфигурации BIOS и убедитесь, что она настроена на загрузку с сети. Этот раздел содержит несколько советов по устранению неполадок для выявления источника проблемы с конфигурацией, если клиенты не могут загрузиться через PXE. [.procedure] **** . Используйте пакет package:net/wireshark[] или порт для отладки сетевого трафика, задействованного в процессе загрузки по PXE, который показан на схеме ниже. + .Процесс загрузки PXE с монтированием корневой файловой системы по NFS image::pxe-nfs.png[] + 1. Клиент рассылает широковещательное сообщение DHCPDISCOVER. + 2. Сервер DHCP отвечает с указанием IP-адреса, next-server, filename и значений root-path. + 3. Клиент отправляет запрос TFTP на next-server, запрашивая получение файла filename. + 4. Сервер TFTP отвечает и отправляет имя файла клиенту. + 5. Клиент выполняет файл `pxeboot(8)`, который затем загружает ядро. При запуске ядра корневая файловая система, указанная в `root-path`, монтируется через NFS. + . На TFTP-сервере прочитайте [.filename]#/var/log/xferlog#, чтобы убедиться, что [.filename]#pxeboot# загружается из правильного места. Для проверки конфигурации, сделанной для этого примера: + [source, shell] .... # tftp 192.168.0.1 tftp> get FreeBSD/install/boot/pxeboot Received 264951 bytes in 0.1 seconds .... + Разделы `BUGS` в man:tftpd[8] и man:tftp[1] описывают некоторые ограничения TFTP. . Убедитесь, что корневая файловая система может быть смонтирована через NFS. Для проверки конфигурации из этого примера: + [source, shell] .... # mount -t nfs 192.168.0.1:/b/tftpboot/FreeBSD/install /mnt .... + . Для загрузки по PXE на UEFI замените файл [.filename]#boot/pxeboot# на файл [.filename]#boot/loader.efi#: [source, shell] .... # chroot ${NFSROOTDIR} # mv boot/pxeboot boot/pxeboot.original # cp boot/loader.efi boot/pxeboot .... **** [[carp]] == Протокол общей избыточности адресов (CARP) Протокол общей избыточности адресов (CARP) позволяет нескольким хостам использовать один и тот же IP-адрес и идентификатор виртуального хоста (VHID) для обеспечения _высокой доступности_ одной или нескольких служб. Это означает, что при отказе одного или нескольких хостов остальные хосты прозрачно возьмут на себя их функции, чтобы пользователи не заметили сбоя в работе службы. В дополнение к общему IP-адресу каждый узел имеет собственный IP-адрес для управления и настройки. Все машины, которые используют общий IP-адрес, имеют одинаковый VHID. для каждого виртуального IP-адреса VHID должен быть уникальным в пределах широковещательного домена сетевого интерфейса. Высокая доступность с использованием CARP встроена в FreeBSD, хотя шаги по её настройке немного различаются в зависимости от версии FreeBSD. В этом разделе приведён одинаковый пример конфигурации для версий до и начиная с FreeBSD 10. Этот пример настраивает поддержку отказоустойчивости с тремя хостами, каждый из которых имеет уникальный IP-адрес, но предоставляет одинаковое веб-содержимое. В нём есть два разных основных хоста с именами `hosta.example.org` и `hostb.example.org`, а также общий резервный хост с именем `hostc.example.org`. Эти машины балансируют нагрузку с помощью конфигурации DNS Round Robin. Основная и резервная машины настроены идентично, за исключением их имён хостов и управляющих IP-адресов. Эти серверы должны иметь одинаковую конфигурацию и запускать одинаковые службы. При возникновении переключения, запросы к службе на общем IP-адресе могут быть корректно обработаны только если резервный сервер имеет доступ к тому же содержимому. Резервная машина имеет два дополнительных интерфейса CARP, по одному для каждого из IP-адресов основного сервера содержимого. При возникновении сбоя, резервный сервер возьмёт IP-адрес вышедшей из строя основной машины. [[carp-10x]] === Использование CARP Включите поддержку CARP при загрузке, добавив запись для модуля ядра [.filename]#carp.ko# в [.filename]#/boot/loader.conf#: [.programlisting] .... carp_load="YES" .... Чтобы сейчас загрузить модуль без перезагрузки: [source, shell] .... # kldload carp .... Для пользователей, которые предпочитают использовать собственное ядро, добавьте следующую строку в файл конфигурации ядра и скомпилируйте его, как описано в crossref:kernelconfig[kernelconfig,Настройка ядра FreeBSD]: [.programlisting] .... device carp .... Имя хоста, управляющий IP-адрес и маска подсети, общий IP-адрес и VHID задаются путем добавления записей в [.filename]#/etc/rc.conf#. Этот пример для `hosta.example.org`: [.programlisting] .... hostname="hosta.example.org" ifconfig_em0="inet 192.168.1.3 netmask 255.255.255.0" ifconfig_em0_alias0="inet vhid 1 pass testpass alias 192.168.1.50/32" .... Следующий набор записей предназначен для `hostb.example.org`. Поскольку он представляет второй мастер, используется другой общий IP-адрес и VHID. Однако пароли, указанные с помощью `pass`, должны быть идентичными, так как CARP будет принимать и обрабатывать объявления только от машин с правильным паролем. [.programlisting] .... hostname="hostb.example.org" ifconfig_em0="inet 192.168.1.4 netmask 255.255.255.0" ifconfig_em0_alias0="inet vhid 2 pass testpass alias 192.168.1.51/32" .... Третья машина, `hostc.example.org`, настроена для обработки перехода на резервный сервер от любого из основных. Эта машина настроена с двумя CARPVHID, по одному для обработки виртуального IP-адреса каждого из основных хостов. Значение advskew (временной сдвиг анонсов CARP) обеспечивает задержку в отправке анонсов резервным сервером по сравнению с основным, поскольку `advskew` контролирует порядок приоритета при наличии нескольких резервных серверов. [.programlisting] .... hostname="hostc.example.org" ifconfig_em0="inet 192.168.1.5 netmask 255.255.255.0" ifconfig_em0_alias0="inet vhid 1 advskew 100 pass testpass alias 192.168.1.50/32" ifconfig_em0_alias1="inet vhid 2 advskew 100 pass testpass alias 192.168.1.51/32" .... Наличие двух настроенных CARPVHID означает, что `hostc.example.org` заметит, если один из главных серверов станет недоступен. Если главный сервер не отправит объявление раньше резервного сервера, резервный сервер возьмёт на себя общий IP-адрес до тех пор, пока главный сервер снова не станет доступен. [NOTE] ==== Если исходный главный сервер снова станет доступен, `hostc.example.org` не освободит виртуальный IP-адрес автоматически. Чтобы это произошло, необходимо включить вытеснение. Эта функция отключена по умолчанию и управляется через переменную man:sysctl[8] `net.inet.carp.preempt`. Администратор может принудительно вернуть IP-адрес главному серверу с резервного сервера: [source, shell] .... # ifconfig em0 vhid 1 state backup .... ==== Как только настройка завершена, перезапустите сеть или перезагрузите каждую систему. Высокая доступность теперь включена. Функциональность CARP может управляться с помощью нескольких переменных man:sysctl[8], описанных в man:carp[4]. Другие действия могут быть запущены при событиях CARP с использованием man:devd[8]. [[network-vlan]] == Виртуальные сети VLAN Виртуальные локальные сети (VLAN) — это способ виртуального разделения сети на множество различных подсетей, также называемый сегментированием. Каждый сегмент будет иметь свою собственную широковещательную область и быть изолированным от других VLAN. На FreeBSD поддержка VLAN должна быть обеспечена драйвером сетевой карты. Чтобы узнать, какие драйверы поддерживают VLAN, обратитесь к странице руководства man:vlan[4]. При настройке VLAN необходимо знать несколько параметров. Во-первых, какой сетевой интерфейс? Во-вторых, какой тег VLAN? Для настройки VLAN во время выполнения, с сетевой картой `em0` и тегом VLAN `5`, команда будет выглядеть следующим образом: [source, shell] .... # ifconfig em0.5 create vlan 5 vlandev em0 inet 192.168.20.20/24 .... [NOTE] ==== Видите, что имя интерфейса состоит из имени драйвера сетевой карты и тега VLAN, разделенных точкой? Это рекомендуемая практика для упрощения работы с конфигурациями VLAN, когда на машине присутствует множество VLAN. ==== [NOTE] ==== При определении VLAN убедитесь, что родительский сетевой интерфейс также настроен и включен. Минимальная конфигурация для приведённого выше примера будет следующей: [source, shell] .... # ifconfig em0 up .... ==== Для настройки VLAN при загрузке необходимо обновить файл [.filename]#/etc/rc.conf#. Чтобы повторить приведённую выше конфигурацию, нужно добавить следующее: [.programlisting] .... vlans_em0="5" ifconfig_em0_5="inet 192.168.20.20/24" .... Дополнительные VLAN могут быть добавлены путём простого дополнения тега в поле `vlans_em0` и добавления дополнительной строки для настройки сети на интерфейсе с этим тегом VLAN. [NOTE] ==== При определении VLAN в [.filename]#/etc/rc.conf# убедитесь, что родительский сетевой интерфейс также настроен и включен. Минимальная конфигурация для приведённого выше примера будет следующей: [.programlisting] .... ifconfig_em0="up" .... ==== Полезно присвоить интерфейсу символическое имя, чтобы при изменении связанного оборудования требовалось обновить лишь несколько переменных конфигурации. Например, камеры наблюдения должны работать через VLAN 1 на `em0`. Позже, если карта `em0` будет заменена на карту с драйвером man:ixgb[4], все упоминания `em0.1` не нужно будет изменять на `ixgb0.1`. Для настройки VLAN `5` на сетевой карте `em0`, назначения интерфейсу имени `cameras` и присвоения интерфейсу IP-адреса `_192.168.20.20_` с `24`-битным префиксом используйте следующую команду: [source, shell] .... # ifconfig em0.5 create vlan 5 vlandev em0 name cameras inet 192.168.20.20/24 .... Для интерфейса с именем `video` используйте следующее: [source, shell] .... # ifconfig video.5 create vlan 5 vlandev video name cameras inet 192.168.20.20/24 .... Чтобы применить изменения при загрузке, добавьте следующие строки в [.filename]#/etc/rc.conf#: [.programlisting] .... vlans_video="cameras" create_args_cameras="vlan 5" ifconfig_cameras="inet 192.168.20.20/24" .... diff --git a/documentation/content/ru/books/handbook/wayland/_index.adoc b/documentation/content/ru/books/handbook/wayland/_index.adoc index 1f8b390f32..eecc699711 100644 --- a/documentation/content/ru/books/handbook/wayland/_index.adoc +++ b/documentation/content/ru/books/handbook/wayland/_index.adoc @@ -1,588 +1,588 @@ --- description: 'Эта глава описывает, как установить и настроить Wayland и композиторы на FreeBSD, что обеспечивает графическую пользовательскую среду' next: books/handbook/network params: path: /books/handbook/wayland/ part: 'В начале' prev: books/handbook/x11 showBookMenu: 'true' tags: ["Wayland", "XWayland", "KDE", "Plasma", "Xfce", "Gnome", "Intel", "AMD", "NVIDIA", "Wayfire", "Sway", "Hikari"] title: 'Глава 6. Wayland' weight: 8 --- [[wayland]] = Wayland на FreeBSD :doctype: book :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumoffset: 6 :partnums: :source-highlighter: rouge :experimental: :images-path: books/handbook/wayland/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] [[wayland-synopsis]] == Обзор Установка FreeBSD с помощью bsdinstall не включает автоматически графический интерфейс пользователя. В этой главе описано, как выбрать, установить и настроить композитор Wayland, который предоставляет графическую среду. Прежде чем читать эту главу, необходимо: * Знать, как установить crossref:ports[ports,дополнительное стороннее программное обеспечение]. * Знать, как определить и настроить crossref:x11[x-graphic-card-drivers,драйверы для вашего графического оборудования]. Прочтите эту главу, чтобы узнать: * Как настроить FreeBSD для размещения графической среды Wayland. * Как установить и настроить композитор Wayland. * Как запускать программы, предназначенные для старой версии X Window System. * Как настроить удалённый доступ к графической среде Wayland. [[wayland-overview]] == Обзор Wayland Wayland — это коммуникационный протокол, который может заменить сервер дисплеев, такой как X.org. Он отличается от X.org несколькими важными способами. Во-первых, Wayland — это только протокол, который выступает в роли посредника между клиентами, используя механизм, который устраняет зависимость от X-сервера. X.org включает в себя как протокол X11, используемый для работы с удалёнными дисплеями, так и X-сервер, который принимает соединения и отображает окна. В случае с Wayland, композитор или оконный менеджер предоставляет сервер дисплеев вместо традиционного X-сервера. Поскольку Wayland не является X-сервером, для удалённого управления рабочим столом традиционные соединения с экраном X потребуют использования других методов, таких как VNC или RDP. Во-вторых, Wayland может управлять композитными взаимодействиями между клиентами и композитором как отдельная сущность, которой не требуется поддержка протоколов X. Wayland относительно нов, и не все программы были обновлены для нативной работы без поддержки `Xwayland`. Поскольку Wayland не предоставляет X-сервер, а ожидает, что композиторы обеспечат эту поддержку, оконные менеджеры X11, которые ещё не поддерживают Wayland, потребуют, чтобы `Xwayland` не запускался с параметром `-rootless`. Удаление параметра `-rootless` восстановит поддержку оконных менеджеров X11. [NOTE] ==== Текущий драйвер NVIDIA(R) должен работать с большинством композиторов wlroots, но может быть немного нестабильным и не поддерживать все функции на данный момент. Требуются добровольцы для помощи в работе над NVIDIA(R) DRM. ==== В настоящее время множество программного обеспечения, включая Firefox, работает с минимальными проблемами в Wayland. Также доступны несколько окружений рабочего стола, например, замена Compiz Fusion под названием Wayfire и замена менеджера окон i3 — Sway. [NOTE] ==== По состоянию на май 2021 года plasma5-kwin поддерживает Wayland в FreeBSD. Для использования Plasma под Wayland используйте параметр `startplasma-wayland` с `ck-launch-session` и подключите dbus следующим образом: `dbus-launch --exit-with-x11 ck-launch-session startplasma-wayland`, чтобы заставить это работать. ==== Для композиторов необходимо наличие ядра с поддержкой драйвера man:evdev[4] для использования функциональности привязки клавиш. Он включён по умолчанию в ядро [.filename]#GENERIC#; однако, если ядро было изменено и поддержка man:evdev[4] была удалена, потребуется загрузить модуль man:evdev[4]. Кроме того, пользователям `Wayland` необходимо быть членами группы `video`. Чтобы быстро внести это изменение, используйте команду `pw`: [source, shell] ---- pw groupmod video -m user ---- Установка Wayland проста; сам протокол не требует значительной настройки. Большая часть композиции будет зависеть от выбранного композитора. Установив `seatd` сейчас, можно пропустить один шаг в процессе установки и настройки композитора, так как `seatd` необходим для предоставления непривилегированного доступа к некоторым устройствам. Все описанные здесь композиторы должны работать с драйверами с открытым исходным кодом package:graphics/drm-kmod[]; однако видеокарты NVIDIA(R) могут иметь проблемы при использовании проприетарных драйверов. Для начала установите следующие пакеты: [source, shell] ---- # pkg install wayland seatd ---- После установки протокола и необходимых пакетов, композитор должен создать пользовательский интерфейс. В следующих разделах будут рассмотрены несколько композиторов. Все композиторы, использующие Wayland, требуют наличия runtime-каталога, определённого в окружении. Начиная с FreeBSD 14.1, он создаётся и определяется автоматически. Для более ранних версий это можно сделать с помощью следующей команды в оболочке bourne: [source, shell] ---- % export XDG_RUNTIME_DIR=/var/run/user/`id -u` ---- Важно отметить, что большинство композиторов ищут файлы конфигурации в каталоге `XDG_RUNTIME_DIR`. В приведённых здесь примерах будет использоваться параметр для указания файла конфигурации в [.filename]#~/.config#, чтобы разделить временные файлы и файлы конфигурации. Рекомендуется настроить псевдоним для каждого композитора, чтобы загружать указанный файл конфигурации. [WARNING] ==== Сообщается, что пользователи ZFS могут столкнуться с проблемами при работе с некоторыми клиентами Wayland, так как им требуется доступ к `posix_fallocate()` в runtime-каталоге. Хотя автор не смог воспроизвести эту проблему на своей системе с ZFS, рекомендуемым решением является отказ от использования ZFS для runtime-каталога и использование `tmpfs` для [.filename]#/var/run#. В этом случае файловая система `tmpfs` используется для [.filename]#/var/run# и монтируется командой `mount -t tmpfs tmpfs /var/run`, после чего это изменение можно сделать постоянным после перезагрузок через [.filename]#/etc/fstab#. Переменная окружения XDG_RUNTIME_DIR может быть настроена на использование [.filename]#/var/run/user/$UID#, чтобы избежать потенциальных проблем с ZFS. Учитывайте этот сценарий при изучении примеров конфигурации в следующих разделах. ==== Демон seatd помогает управлять доступом к общим системным устройствам для непривилегированных пользователей в композиторах; это включает графические карты. Для традиционных менеджеров X11, таких как Plasma и GNOME, `seatd` не требуется, но для обсуждаемых здесь композиторов Wayland он должен быть включён в системе и работать перед запуском окружения композитора. Чтобы включить и запустить демон `seatd` сейчас и при инициализации системы: [source, shell] ---- # sysrc seatd_enable="YES" # service seatd start ---- После этого необходимо установить композитор, который аналогичен рабочему столу X11, для графической среды. Здесь рассматриваются три варианта, включая базовые настройки, настройку блокировки экрана и рекомендации для получения дополнительной информации. [[wayland-wayfire]] == Композитор Wayfire Wayfire — это композитор, который стремится быть легковесным и настраиваемым. Доступно несколько функций, и он возвращает некоторые элементы из ранее выпущенного рабочего стола Compiz Fusion. Все части выглядят красиво на современном оборудовании. Чтобы запустить Wayfire, начните с установки необходимых пакетов: [source, shell] ---- # pkg install wayfire wf-shell alacritty swaylock-effects swayidle wlogout kanshi mako wlsunset ---- Пакет `alacritty` предоставляет эмулятор терминала. Однако он не является строго обязательным, так как другие эмуляторы терминалов, такие как `kitty` и `Terminal` XFCE-4, были протестированы и подтверждены для работы под композитором Wayfire. Конфигурация Wayfire относительно проста; она использует файл, который следует изучить перед внесением любых изменений. Для начала скопируйте пример файла в каталог конфигурации среды выполнения, а затем отредактируйте файл: [source, shell] ---- % mkdir ~/.config/wayfire % cp /usr/local/share/examples/wayfire/wayfire.ini ~/.config/wayfire ---- Настройки по умолчанию подойдут большинству пользователей. В файле конфигурации уже предустановлены параметры, такие как известный `cube`, а также приведены инструкции по доступным настройкам. Вот несколько основных параметров, на которые стоит обратить внимание: [.programlisting] .... [output] mode = 1920x1080@60000 position = 0,0 transform = normal scale = 1.000000 .... В этом примере из файла конфигурации вывод на экран должен соответствовать указанному режиму с указанной частотой. Например, параметр mode должен быть установлен как `ширинаxвысота@частота_обновления`. Параметр position размещает вывод в указанной позиции (в пикселях). Значение по умолчанию подходит для большинства пользователей. Наконец, transform задаёт фоновое преобразование, а scale масштабирует вывод до указанного коэффициента. Значения по умолчанию для этих параметров обычно приемлемы; дополнительную информацию см. в документации. Как уже упоминалось, Wayland — это новая технология, и не все приложения пока работают с этим протоколом. На данный момент `sddm` не поддерживает запуск и управление композиторами в Wayland. В этих примерах вместо него использовалась утилита `swaylock`. Файл конфигурации содержит параметры для запуска `swayidle` и `swaylock` для управления простоем и блокировкой экрана. Эта опция для определения действия при простое системы указана как: [.programlisting] .... idle = swaylock .... И таймаут блокировки настраивается с помощью следующих строк: [.programlisting] .... [idle] toggle = KEY_Z screensaver_timeout = 300 dpms_timeout = 600 .... Первый вариант заблокирует экран через 300 секунд, а через следующие 300 секунд экран выключится с помощью опции `dpms_timeout`. Последнее, на что стоит обратить внимание, — это клавиша . В большинстве конфигураций упоминается эта клавиша, и она соответствует традиционной клавише `Windows` на клавиатуре. На большинстве клавиатур эта клавиша super доступна; однако, если её нет, её следует переназначить в этом конфигурационном файле. Например, чтобы заблокировать экран, зажмите клавишу super, клавишу kbd:[shift] и нажмите клавишу kbd:[escape]. Если сопоставления не изменены, это запустит приложение swaylock. Конфигурация по умолчанию для `swaylock` отображает серый экран; однако приложение легко настраивается и хорошо документировано. Кроме того, поскольку была установлена версия swaylock-effects, доступно несколько дополнительных возможностей, таких как эффект размытия, который можно увидеть, используя следующую команду: [source, shell] ---- % swaylock --effect-blur 7x5 ---- Также есть параметр `--clock`, который отображает часы с датой и временем на экране блокировки. При установке пакета package:x11/swaylock-effects[] включается конфигурация [.filename]#pam.d# по умолчанию. Она предоставляет стандартные настройки, которые подойдут большинству пользователей. Доступны и более расширенные варианты; дополнительную информацию можно найти в документации PAM. На этом этапе пришло время протестировать Wayfire и проверить, сможет ли он запуститься в системе. Просто введите следующую команду: [source, shell] ---- % wayfire -c ~/.config/wayfire/wayfire.ini ---- Теперь композитор должен запуститься и отобразить фоновое изображение вместе с панелью меню в верхней части экрана. Wayfire попытается перечислить установленные совместимые приложения для рабочего стола и отобразит их в этом выпадающем меню; например, если установлен файловый менеджер XFCE-4, он появится в этом меню. Если конкретное приложение совместимо и достаточно важно для назначения горячей клавиши, его можно привязать к комбинации клавиш с помощью конфигурационного файла [.filename]#wayfire.ini#. Wayfire также имеет инструмент настройки под названием Wayfire Config Manager. Он доступен в выпадающем меню, но его также можно запустить через терминал, выполнив следующую команду: [source, shell] ---- % wcm ---- Различные параметры конфигурации Wayfire, включая специальные эффекты композитора, могут быть включены, отключены или настроены через это приложение. Кроме того, для более удобного взаимодействия, в конфигурационном файле могут быть активированы менеджер фона, панель и док-приложение: [.programlisting] .... panel = wf-panel dock = wf-dock background = wf-background .... [WARNING] ==== Изменения, внесенные через `wcm`, перезапишут пользовательские настройки в конфигурационном файле [.filename]#wayfire.ini#. Настоятельно рекомендуется создать резервную копию файла [.filename]#wayfire.ini#, чтобы можно было восстановить важные изменения. ==== Наконец, стандартный лаунчер, указанный в [.filename]#wayfire.ini#, — это package:x11/wf-shell[], который может быть заменён другими панелями по желанию пользователя. [[wayland-hikari]] == Композитор Hikari Композитор Hikari использует несколько концепций, ориентированных на продуктивность, такие как листы (sheets), рабочие пространства (workspaces) и другие. В этом отношении он напоминает тайловый оконный менеджер. Если разбирать подробнее, композитор начинается с одного рабочего пространства, которое аналогично виртуальным рабочим столам. Hikari использует одно рабочее пространство (или виртуальный рабочий стол) для взаимодействия с пользователем. Рабочее пространство состоит из нескольких представлений (views) — это рабочие окна в композиторе, сгруппированные либо в листы (sheets), либо в группы (groups). И листы, и группы состоят из набора представлений — то есть окон, объединённых вместе. При переключении между листами или группами активный лист или группа становятся рабочим пространством. В руководстве (man-странице) эти функции описаны более подробно, но в данном документе достаточно рассматривать одно рабочее пространство с одним листом. Установка Hikari включает один пакет package:x11-wm/hikari[] и терминальный эмулятор `alacritty`: [source, shell] ---- # pkg install hikari alacritty ---- [NOTE] ==== Другие оболочки, такие как `kitty` или `Terminal` в Plasma, будут работать под Wayland. Пользователям стоит поэкспериментировать с предпочитаемым терминальным редактором, чтобы проверить совместимость. ==== Hikari использует файл конфигурации [.filename]#hikari.conf#, который может быть размещен либо в XDG_RUNTIME_DIR, либо указан при запуске с помощью параметра `-c`. Файл конфигурации автозапуска не обязателен, но может немного упростить переход на этот композитор. Начало настройки заключается в создании каталога конфигурации Hikari и копировании файла конфигурации для редактирования: [source, shell] ---- % mkdir ~/.config/hikari % cp /usr/local/etc/hikari/hikari.conf ~/.config/hikari ---- Конфигурация разбита на различные разделы, такие как `ui`, `outputs`, `layouts` и другие. Для большинства пользователей значений по умолчанию будет достаточно, однако некоторые важные изменения всё же стоит внести. Например, переменная `$TERMINAL` обычно не установлена в окружении пользователя. Измените эту переменную или отредактируйте файл [.filename]#hikari.conf#, указав: [.programlisting] .... terminal = "/usr/local/bin/alacritty" .... Запустит терминал `alacritty` при нажатии связанной клавиши. При изучении конфигурационного файла следует обратить внимание, что заглавные буквы используются для сопоставления клавиш пользователю. Например, клавиша kbd:[L] для запуска терминала kbd:[L+Return] на самом деле является ранее упомянутой клавишей super или клавишей с логотипом Windows. Таким образом, удерживая kbd:[L/super/Windows] и нажимая kbd:[Enter], вы откроете указанный эмулятор терминала с конфигурацией по умолчанию. Для сопоставления других клавиш приложениям необходимо создать определение действия. Для этого пункт действия должен быть указан в разделе actions, например: [.programlisting] .... actions { terminal = "/usr/local/bin/alacritty" browser = "/usr/local/bin/firefox" } .... Затем действие может быть отображено в разделе `keyboard`, который определен внутри раздела `bindings`: [.programlisting] .... bindings { keyboard { SNIP "L+Return" = action-terminal "L+b" = action-browser SNIP .... После перезапуска Hikari удержание кнопки с логотипом Windows и нажатие клавиши kbd:[b] на клавиатуре запустит веб-браузер. Композитор не имеет строки меню, и рекомендуется настроить, как минимум, терминальный эмулятор перед миграцией. Руководство содержит множество документации, его следует прочитать перед полной миграцией. Ещё один положительный аспект Hikari заключается в том, что при переходе на этот композитор его можно запускать в средах рабочего стола Plasma и GNOME, что позволяет опробовать его перед полной миграцией. Заблокировать экран в Hikari просто, так как пакет включает файл конфигурации [.filename]#pam.d# и утилиту разблокировки. Сочетание клавиш для блокировки экрана — kbd:[L] (клавиша с логотипом Windows)+ kbd:[Shift] + kbd:[Backspace]. Следует отметить, что все представления, не помеченные как публичные, будут скрыты. Эти представления не будут принимать ввод, когда экран заблокирован, но будьте осторожны с отображением конфиденциальной информации. Некоторым пользователям может быть проще перейти на другую утилиту блокировки экрана, например swaylock-effects, которая обсуждается в этом разделе. Чтобы запустить Hikari, используйте следующую команду: [source, shell] ---- % hikari -c ~/.config/hikari/hikari.conf ---- [[wayland-sway]] == Композитор Sway Композитор Sway — это тайловый композитор, предназначенный для замены оконного менеджера i3. Он должен работать с текущей конфигурацией i3 пользователя, однако для использования новых функций может потребоваться дополнительная настройка. Перед установкой Sway убедитесь, что графическая карта (GPU) установлена и настроена правильно. Обратитесь к разделу crossref:x11[x-graphic-card-drivers,Драйверы видеокарт] для получения инструкций. Этот шаг необходим для корректной работы композитора Sway. В следующих примерах предполагается новая установка без переноса каких-либо конфигураций i3. Для установки Sway и полезных компонентов выполните следующую команду от имени пользователя root: [source, shell] ---- # pkg install sway swayidle swaylock-effects alacritty dmenu-wayland dmenu ---- Для базового файла конфигурации выполните следующие команды, а затем отредактируйте файл конфигурации после его копирования: [source, shell] ---- % mkdir ~/.config/sway % cp /usr/local/etc/sway/config ~/.config/sway ---- Базовый файл конфигурации содержит множество настроек по умолчанию, которые подойдут большинству пользователей. Однако следует внести несколько важных изменений, таких как: [.programlisting] .... # Logo key. Use Mod1 for Alt. input * xkb_rules evdev set $mod Mod4 # The preferred terminal emulator set $term alacritty set $lock swaylock -f -c 000000 output "My Workstation" mode 1366x768@60Hz position 1366 0 output * bg ~/wallpapers/mywallpaper.png stretch ### Idle configuration exec swayidle -w \ timeout 300 'swaylock -f -c 000000' \ timeout 600 'swaymsg "output * dpms off"' resume 'swaymsg "output * dpms on"' \ before-sleep 'swaylock -f -c 000000' .... В предыдущем примере загружаются правила `xkb` для событий man:evdev[4], а клавиша $mod устанавливается в значении логотипа Windows для привязки клавиш. Далее эмулятор терминала был установлен как `alacritty`, и определена команда блокировки экрана; подробнее об этом позже. Ключевое слово `output`, режим, позиция, фоновое изображение, а также Sway указано растягивать это изображение для заполнения экрана. Наконец, `swayidle` настроен на работу в режиме демона и блокировку экрана после таймаута в 300 секунд, переводя экран или монитор в режим сна после 600 секунд. Здесь также определён цвет заблокированного фона 000000, что соответствует чёрному. С использованием swaylock-effects можно также отображать часы с параметром `--clock`. Дополнительные параметры см. на странице руководства. Также следует ознакомиться с man:sway-output[5]; она содержит множество информации по настройке доступных параметров вывода. В Sway, чтобы вызвать меню приложений, удерживайте клавишу с логотипом Windows (mod) и нажмите kbd:[d]. Меню можно перемещаться с помощью клавиш со стрелками на клавиатуре. Также есть способ изменить расположение панели и добавить трей; подробнее см. на man:sway-bar[5]. В стандартной конфигурации в верхнем правом углу отображается дата и время. Пример можно найти в разделе `Bar` конфигурационного файла. По умолчанию конфигурация не включает блокировку экрана, за исключением приведённого выше примера, а также таймер блокировки. Для создания привязки клавиши блокировки добавьте следующую строку в раздел `Key bindings`: [.programlising] .... # Lock the screen manually bindsym $mod+Shift+Return exec $lock .... Теперь экран можно заблокировать с помощью комбинации: удерживая клавишу с логотипом Windows, нажать и удерживать Shift, а затем нажать Enter. При установке Sway, будь то из пакета или коллекции портов FreeBSD, устанавливается файл по умолчанию для [.filename]#pam.d#. Конфигурация по умолчанию подходит для большинства пользователей, но доступны и более продвинутые варианты. Для получения дополнительной информации ознакомьтесь с документацией PAM. Наконец, чтобы выйти из Sway и вернуться к оболочке, удерживайте клавишу с логотипом Windows, клавишу Shift и нажмите kbd:[e]. Появится запрос с возможностью выхода из Sway. Во время миграции Sway можно запустить через эмулятор терминала в X11-окружении, например, Plasma. Это упрощает тестирование различных изменений и сочетаний клавиш перед полным переходом на этот композитор. Чтобы запустить Sway, выполните следующую команду: [source, shell] ---- % sway -c ~/.config/sway/config ---- [[wayland-xwayland]] == Использование Xwayland При установке Wayland бинарный файл `Xwayland` должен быть установлен, если только Wayland не собран без поддержки X11. Если файл [.filename]#/usr/local/bin/Xwayland# отсутствует, установите его с помощью следующей команды: [source, shell] ---- # pkg install xwayland ---- [NOTE] ==== Рекомендуется использовать разрабатываемую версию Xwayland, которая, скорее всего, была установлена вместе с пакетом Wayland. Каждый композитор имеет свой способ включения или отключения этой функции. ==== После установки `Xwayland` настройте его в выбранном композиторе. Для Wayfire в файле [.filename]#wayfire.ini# требуется следующая строка: [.programlisting] .... xwayland = true .... Для композитора Sway `Xwayland` должен быть включен по умолчанию. Тем не менее, рекомендуется вручную добавить строку конфигурации в файл [.filename]#~/.config/sway/config#, например, следующую: [.programlisting] ..... xwayland enable ..... Наконец, для Hikari не требуется никаких изменений. Поддержка `Xwayland` включена по умолчанию. Чтобы отключить эту поддержку, пересоберите пакет из коллекции портов и отключите поддержку Xwayland во время сборки. После внесения этих изменений запустите композитор из командной строки и откройте терминал с помощью назначенных клавиш. В этом терминале выполните команду `env` и найдите переменные `DISPLAY`. Если композитор успешно запустил X-сервер Xwayland, эти переменные окружения должны выглядеть примерно следующим образом: [source, shell] ---- % env | grep DISPLAY ---- [.programlisting] .... WAYLAND_DISPLAY=wayland-1 DISPLAY=:0 .... В этом выводе есть дисплей Wayland по умолчанию и дисплей, настроенный для сервера Xwayland. Другой способ проверить, что `Xwayland` работает корректно, — установить и протестировать небольшой package:[x11/eyes], а затем проверить вывод. Если приложение `xeyes` запускается и глаза следят за указателем мыши, значит, Xwayland работает правильно. Если же отображается ошибка, например, следующая, значит, что-то произошло во время инициализации `Xwayland`, и, возможно, его потребуется переустановить: [.programlisting] .... Error: Cannot open display wayland-0 .... [WARNING] ==== Одной из особенностей безопасности Wayland является отсутствие дополнительного сетевого слушателя при отсутствии запущенного X-сервера. После включения `Xwayland` данная особенность безопасности перестает применяться в системе. ==== Для некоторых композиторов, таких как Wayfire, `Xwayland` может не запуститься правильно. В таком случае, `env` покажет следующую информацию о переменных окружения `DISPLAY`: [source, shell] ---- % env | grep DISPLAY ---- [.programlisting] .... DISPLAY=wayland-1 WAYLAND_DISPLAY=wayland-1 .... Хотя `Xwayfire` был установлен и настроен, приложения X11 не запускаются, выдавая ошибку дисплея. Чтобы обойти эту проблему, убедитесь, что уже существует экземпляр `Xwayland`, использующий UNIX-сокет, с помощью двух методов. Сначала проверьте вывод команды `sockstat` и найдите X11-unix: [source, shell] ---- % sockstat | grep x11 ---- Должна быть информация, аналогичная следующей: [.programlisting] .... trhodes Xwayland 2734 8 stream /tmp/.X11-unix/X0 trhodes Xwayland 2734 9 stream /tmp/.X11-unix/X0 trhodes Xwayland 2734 10 stream /tmp/.X11-unix/X0 trhodes Xwayland 2734 27 stream /tmp/.X11-unix/X0_ trhodes Xwayland 2734 28 stream /tmp/.X11-unix/X0 .... Это указывает на наличие сокета X11. Это можно дополнительно проверить, попытавшись вручную запустить `Xwayland` в эмуляторе терминала, работающем под композитором: [source, shell] ---- % Xwayland ---- Если сокет X11 уже доступен, пользователю должно быть показано следующее сообщение об ошибке: [.programlisting] .... (EE) Fatal server error: (EE) Server is already active for display 0 If this server is no longer running, remove /tmp/.X0-lock and start again. (EE) .... Поскольку доступен активный X-дисплей с использованием дисплея ноль, переменная окружения была просто задана неправильно. Чтобы исправить это, измените переменную окружения `DISPLAY` на `:0` и попробуйте запустить приложение снова. В следующем примере используется пакет package:mail/claws-mail[] в качестве приложения, которому требуется сервис `Xwayland`: [source, shell] ---- export DISPLAY=:0 ---- После этого изменения приложение package:mail/claws-mail[] должно начать использовать `Xwayland` и работать как ожидается. [[wayland-remotedesktop]] == Удалённый рабочий стол с использованием VNC Ранее в этом документе упоминалось, что Wayland не предоставляет такой же доступ в стиле X-сервера, как Xorg. Вместо этого пользователи могут выбрать любой протокол удалённого рабочего стола, например RDP или VNC. Коллекция портов FreeBSD включает `wayvnc`, который поддерживает композиторы на основе wlroots, такие как рассмотренные здесь. Это приложение можно установить с помощью: [source, shell] ---- # pkg install wayvnc ---- В отличие от некоторых других пакетов, `wayvnc` не поставляется с файлом конфигурации. К счастью, справочная страница содержит описание важных параметров, и их можно использовать для создания простого файла конфигурации: [.programlisting] .... address=0.0.0.0 enable_auth=true username=username password=password private_key_file=/path/to/key.pem certificate_file=/path/to/cert.pem .... Необходимо сгенерировать ключевые файлы, и настоятельно рекомендуется их использовать для повышения безопасности соединения. При запуске wayvnc будет искать файл конфигурации в [.filename]#~/.config/wayvnc/config#. Это можно переопределить с помощью опции `-C файл_конфигурации` при запуске сервера. Таким образом, чтобы запустить сервер `wayvnc`, выполните следующую команду: [source, shell] ---- % wayvnc -C ~/.config/wayvnc/config ---- [NOTE] ==== На момент написания этого документа нет rc.d-скрипта для запуска `wayvnc` при инициализации системы. Если требуется такая функциональность, необходимо создать локальный стартовый файл. Вероятно, это следует оформить как запрос на добавление функции для сопровождающего порта. ==== [[wayland-ly]] == Логин менеджер Wayland Хотя существует несколько менеджеров входа в систему, которые постепенно переходят на Wayland, одним из вариантов является текстовый менеджер входа package:x11/ly[]. Требуя минимальной настройки, `ly` запускает Sway, Wayfire и другие окружения, отображая окно входа при инициализации системы. Для установки `ly` выполните следующую команду: [source, shell] ---- # pkg install ly ---- Будут представлены некоторые подсказки по настройке, основные шаги - добавить следующие строки в [.filename]#/etc/gettytab#: -[programlisting] +[.programlisting] .... Ly:\ :lo=/usr/local/bin/ly:\ :al=root: .... И затем измените строку ttyv1 в файле [.filename]#/etc/ttys#, чтобы она соответствовала следующей строке: -[programlisting] +[.programlisting] .... ttyv1 "/usr/libexec/getty Ly" xterm onifexists secure .... После перезагрузки системы должно появиться приглашение на вход. Для настройки конкретных параметров, таких как язык, отредактируйте файл [.filename]#/usr/local/etc/ly/config.ini#. Как минимум, в этом файле должен быть указан tty, который был ранее задан в [.filename]#/etc/ttys#. [NOTE] ==== Если для терминала ttyv0 настроен вход в систему, может потребоваться нажать клавиши kbd:[alt] и kbd:[F1], чтобы корректно отображалось окно входа. ==== Когда появляется окно входа, использование стрелок влево и вправо позволяет переключаться между различными поддерживаемыми оконными менеджерами. [[wayland-utilities]] == Полезные утилиты Один полезный инструмент для Wayland, который могут использовать все композиторы, — это `waybar`. Хотя Wayfire и поставляется с меню запуска, удобная и быстрая панель задач — это полезное дополнение для любого композитора или менеджера рабочего стола. `waybar` — это совместимая с Wayland панель задач, которая быстро работает и легко настраивается. Чтобы установить пакет и вспомогательную утилиту управления аудио, выполните следующую команду: [source, shell] ---- # pkg install pavucontrol waybar ---- Для создания каталога конфигурации и копирования стандартного файла конфигурации выполните следующие команды: [source, shell] ---- % mkdir ~/.config/waybar % cp /usr/local/etc/xdg/waybar/config ~/.config/waybar ---- Утилита `lavalauncher` предоставляет панель запуска для различных приложений. Вместе с пакетом не поставляется пример файла конфигурации, поэтому необходимо выполнить следующие действия: [source, shell] ---- mkdir ~/.config/lavalauncher ---- Пример файла конфигурации, который включает только Firefox и расположен справа, приведен ниже: [.programlising] .... global-settings { watch-config-file = true; } bar { output = eDP-1; position = bottom; background-colour = "#202020"; # Condition for the default configuration set. condition-resolution = wider-than-high; config { position = right; } button { image-path = /usr/local/lib/firefox/browser/chrome/icons/default/default48.png; command[mouse-left] = /usr/local/bin/firefox; } button { image-path = /usr/local/share/pixmaps/thunderbird.png; command[mouse-left] = /usr/local/bin/thunderbird; } .... diff --git a/documentation/content/ru/books/handbook/x11/_index.adoc b/documentation/content/ru/books/handbook/x11/_index.adoc index 1d17411b7b..4ea8f4a30d 100644 --- a/documentation/content/ru/books/handbook/x11/_index.adoc +++ b/documentation/content/ru/books/handbook/x11/_index.adoc @@ -1,871 +1,871 @@ --- description: 'Эта глава описывает, как установить и настроить систему X Windows, которая предоставляет графическое окружение' next: books/handbook/wayland params: path: /books/handbook/x11/ part: 'В начале' prev: books/handbook/ports showBookMenu: 'true' tags: ["AMD", "DRM", "Fonts", "Graphics", "Input", "Intel", "Monitor", "NVIDIA", "PRIME", "SCFB", "TrueType", "VESA", "Video", "X11", "Xf86", "Xorg"] title: 'Глава 5. Система X Window' weight: 7 --- [[x11]] = Система X Window :doctype: book :toc: macro :toclevels: 2 :icons: font :sectnums: :sectnumoffset: 5 :partnums: :source-highlighter: rouge :experimental: :images-path: books/handbook/x11/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] [[x11-synopsis]] == Обзор crossref:bsdinstall[bsdinstall-synopsis,Установка] FreeBSD с помощью man:bsdinstall[8] не включает автоматическую установку графического интерфейса пользователя. В этой главе описано, как установить и настроить man:Xorg[1], который предоставляет открытую реализацию системы X Window (часто сокращается до X11), используемую для создания графической среды. Прежде чем читать эту главу, необходимо: * Знать, как устанавливать дополнительное стороннее программное обеспечение, как описано в crossref:ports[ports,Установка приложений: Пакеты и Порты]. Прочтите эту главу, чтобы узнать: * Как выбрать и установить драйверы для графического процессора (GPU). * Различные компоненты X Window System и их взаимодействие. * Как установить и настроить сервер X.org. * Как установить и настроить шрифты в системе X Windows. [[x-graphic-card-drivers]] == Драйверы видеокарт _**аннотация**: Определите свой графический процессор, порт, предоставляющий для него драйвер, установите его, затем включите его запуск при последующей загрузке с помощью man:sysrc[8]._ Прежде чем FreeBSD сможет отображать графическую среду, ей необходим модуль ядра для управления графическим процессором. Графические драйверы являются быстро развивающимся кроссплатформенным программным обеспечением, поэтому они разрабатываются и распространяются отдельно от базовой системы FreeBSD. Следующая таблица показывает различные графические процессоры, поддерживаемые FreeBSD, их соответствующие модули, и порт предоставляет их поддержку: .Поддерживаемые графические устройства [options="header", cols="1,1,1,1"] |=== | Тип | Лицензия | Модуль | Порт | Intel(R) | Открытое программное обеспечение | `i915kms` | package:graphics/drm-kmod[] | AMD(R) | Открытое программное обеспечение | `amdgpu` или `radeonkms` | package:graphics/drm-kmod[] | NVIDIA(R) | Проприетарный | `nvidia-drm`, `nvidia-modeset` или `nvidia` | package:graphics/nvidia-drm-kmod[] или + package:x11/nvidia-driver[] | Буфер кадров системной консоли | Открытое программное обеспечение | `scfb` | package:x11-drivers/xf86-video-scfb[] | Расширение VESA BIOS | Открытое программное обеспечение | `vesa` | package:x11-drivers/xf86-video-vesa[] | VirtualBox(R) | Открытое программное обеспечение | `vboxvideo` | package:emulators/virtualbox-ose-additions[] | VMware(R) | Открытое программное обеспечение | `vmwgfx` | package:x11-drivers/xf86-video-vmware[] |=== Поддерживается несколько поколений технологий драйверов. * Драйверы прямой визуализации (Direct Rendering), позволяющие использовать PRIME offloading. PRIME позволяет сосуществовать нескольким поставщикам графической обработки. PRIME подробно описан в <>. * Kernel Modesetting (crossref:glossary[kms-glossary,KMS]) Это позволяет драйверу напрямую задавать режим отображения. Это необходимо для поддержки приостановки и возобновления работы при использовании драйвера консоли man:vt[4]. * User Modesetting Самая старая категория драйверов по-прежнему поддерживается, однако они могут использоваться только с консолью man:sc[4] и более старыми версиями графической среды man:Xorg[1]. Следующая команда позволяет определить, какой графический процессор установлен в системе: [source, shell] .... % pciconf -lv | grep -B3 display .... Вывод должен быть похож на следующий: [.programlisting] .... vgapci1@pci0:0:2:0: class=0x030000 rev=0x0c hdr=0x00 vendor=0x8086 device=0x46a6 subvendor=0x1028 subdevice=0x0b29 vendor = 'Intel Corporation' device = 'Alder Lake-P GT2 [Iris Xe Graphics]' class = display .... Подробные инструкции по установке и включению этих драйверов приведены в следующих подразделах. [WARNING] ==== Если графический процессор не поддерживается драйверами Intel(R), AMD(R) или NVIDIA(R), следует использовать модули SCFB или VESA. Модуль SCFB должен использоваться при загрузке в режиме UEFI, а модуль VESA — при загрузке в режиме BIOS. Эту команду можно использовать для проверки режима загрузки: [source, shell] .... % sysctl machdep.bootmethod .... Вывод должен быть похож на следующий: [.programlisting] .... machdep.bootmethod: UEFI .... ==== [[x-configuration-intel]] === Intel(R) Graphics Пакет package:graphics/drm-kmod[] косвенно предоставляет набор модулей ядра для использования с Intel(R) Graphics. В последних версиях эти модули могут работать совместно с другими графическими процессорами в режиме PRIME без дополнительной настройки. Драйвер Intel(R) Graphics можно установить, выполнив следующую команду: [source, shell] .... # pkg install drm-kmod .... Затем добавьте модуль в файл [.filename]#/etc/rc.conf#, выполнив следующую команду: [source, shell] .... # sysrc kld_list+=i915kms .... [[x-configuration-amd]] === AMD(R) Graphics Пакет package:graphics/drm-kmod[] косвенно предоставляет модули ядра для набора графических процессоров AMD(R). В зависимости от поколения оборудования могут использоваться модули `amdgpu` или `radeonkms`. Проект FreeBSD поддерживает link:https://wiki.freebsd.org/Graphics/AMD-GPU-Matrix[матрицу поддержки AMD graphics], чтобы показать уровень поддержки и для определения необходимого драйвера. Драйверы AMD(R) Graphics можно установить, выполнив следующую команду: [source, shell] .... # pkg install drm-kmod .... Активируйте текущий модуль, добавив его в файл [.filename]#/etc/rc.conf#, для чего необходимо выполнить следующую команду: [source, shell] .... # sysrc kld_list+=amdgpu .... Для старых видеокарт (до HD7000/Tahiti) активируйте старый модуль, добавив модуль в файл [.filename]#/etc/rc.conf#, выполнив следующую команду: [source, shell] .... # sysrc kld_list+=radeonkms .... [[x-configuration-nvidia]] === NVIDIA(R) Graphics NVIDIA(R) выпускает автономные или дискретные графические процессоры и предоставляет проприетарный драйвер для FreeBSD. Коллекция портов FreeBSD предоставляет более десятилетия драйверов для поддержки поколений графики NVIDIA. Администраторам следует устанавливать последнюю версию драйвера, поддерживаемую их оборудованием. Следующая таблица показывает порт, содержащий драйвер, рекомендуемый для загрузки модуль ядра, и ссылку на список оборудования, поддерживаемого этим драйвером: .Поддерживаемые версии драйверов NVIDIA(R) Graphics [options="header", cols="1,1,1"] |=== | Порт | Модуль | Поддерживаемое оборудование | package:graphics/nvidia-drm-kmod[] | `nvidia` или + `nvidia-modeset` | link:https://www.nvidia.com/Download/driverResults.aspx/210651/en-us/[поддерживаемое оборудование] | package:x11/nvidia-driver-470[] | `nvidia-modeset` | link:https://www.nvidia.com/Download/driverResults.aspx/194639/en-us/[поддерживаемое оборудование] | package:x11/nvidia-driver-390[] или + package:x11/nvidia-secondary-driver-390[] | `nvidia-modeset` | link:https://www.nvidia.com/Download/driverResults.aspx/191122/en-us/[поддерживаемое оборудование] | package:x11/nvidia-driver-340[] | `nvidia` | link:https://www.nvidia.com/Download/driverResults.aspx/156167/en-us/[поддерживаемое оборудование] | package:x11/nvidia-driver-304[] | `nvidia` | link:https://www.nvidia.com/Download/driverResults.aspx/123712/en-us/[поддерживаемое оборудование] |=== Последнюю версию драйвера NVIDIA(R) Graphics можно установить, выполнив следующую команду: [source, shell] .... # pkg install nvidia-drm-kmod .... Чтобы активировать драйвер, добавьте модуль в файл [.filename]#/etc/rc.conf#, выполнив следующую команду: [source, shell] .... # sysrc kld_list+=nvidia-drm .... Это драйвер прямой визуализации crossref:glossary[kms-glossary,KMS]. Kernel modesetting — это опция для установки графического режима в ядре. Включите её для последующих загрузок с помощью следующей настройки man:loader.conf[5]: [source, shell] .... hw.nvidiadrm.modeset="1" .... Как PRIME, так и crossref:wayland[wayland-synopsis,Wayland] требуют режим kernel modesetting. Предыдущие версии драйвера не поддерживают прямую визуализацию (Direct Rendering). Вместо этого используйте модуль modesetting, выполнив следующую команду: [source, shell] .... # sysrc kld_list+=nvidia-modeset .... Если требуются драйверы Nvidia версии ниже 390, учтите, что они не поддерживают режим kernel modesetting, и поэтому должны использоваться с устаревшим драйвером консоли man:sc[4] и версией package:x11/xorg-server[] ниже 1.20. Затем добавьте модуль в файл [.filename]#/etc/rc.conf#, выполнив следующую команду: [source, shell] .... # sysrc kld_list+=nvidia .... [[firmware]] Некоторым графическим картам требуется специальная прошивка, которую необходимо загрузить для корректной работы устройства. Утилита man:fwget[8] может загрузить и установить необходимые файлы в каталог [.filename]#/boot/firmware#. man:fwget[8] выполнит сканирование устройств 'PCI' и загрузит прошивку для тех устройств, которым она нужна. [[x-overview]] == Обзор системы X Window Система X Window представляет собой наследуемый графический стек для платформ UNIX(R), поддерживающий новейшие технологии при сохранении совместимости с приложениями, созданными за многие поколения. Приложения, включая компоненты рабочего стола, обслуживаются сервером man:Xorg[1]. Данная система обладает сетевыми возможностями, и её различные компоненты могут взаимодействовать через сети. [[x-install]] == Установка сервера X.org _**аннотация**: Для размещения crossref:desktop[desktop-synopsis,рабочего стола] должен быть установлен сервер package:x11/xorg[X.org]. Пользователи должны быть добавлены в группу `video` для его использования._ После установки и включения графического драйвера сервер X.org может быть установлен как метапакет или скомпилирован локально с помощью дерева портов. Полный метапакет можно быстро установить, но с меньшими возможностями для настройки: [source, shell] .... # pkg install xorg .... При такой установке будет установлена полная система X Window System, включая традиционный оконный менеджер man:twm[1] и сопутствующий традиционный набор приложений для рабочего стола. Большинству пользователей захочется установить и настроить современный crossref:desktop[desktop-synopsis,рабочий стол] по своему выбору. Текущий пользователь должен быть членом группы `video`, чтобы запускать графическую среду. Чтобы добавить пользователя в группу `video`, выполните следующую команду: [source, shell] .... # pw groupmod video -m username .... Для запуска системы X Window System используйте man:startx[1] из пакета package:x11/xinit[], либо установите и настройте дисплейный менеджер для запуска графического входа при загрузке. [TIP] ==== Меньшая версия системы X Windows, подходящая для опытных пользователей, доступна в пакете package:x11/xorg-minimal[]. Большинство документов, библиотек и приложений установлено не будет. Некоторым приложениям требуются эти дополнительные компоненты для работы. ==== [[x-config]] == Конфигурация X.org _**аннотация**: Если настройки по умолчанию для монитора или устройств ввода не устраивают, в crossref:desktop[desktop-synopsis,рабочем окружении] включены графические интерфейсы для их настройки, либо их можно настроить вручную._ Сервер X.org поддерживает большинство распространённых графических процессоров, мониторов и устройств ввода. Сначала попробуйте настройки по умолчанию. В этом подразделе представлен обзор их конфигурации. [[x-config-files]] === Файлы конфигурации X.org Исторически сервер X.org настраивался с помощью файлов в [.filename]#/usr/local/etc/X11/#. Это до сих пор поддерживается для особых случаев, но конфликтует с динамической автоконфигурацией. Не создавайте конфигурацию сервер X.org в файле [.filename]#xorg.conf# и не запускайте `Xorg -configure`, если автоматическая настройка не дала сбоев. Сервер X.org ищет конфигурационные файлы в нескольких каталогах. [.filename]#/usr/local/etc/X11/# — это *рекомендуемый* каталог для таких файлов в FreeBSD. Использование этого каталога помогает отделять файлы приложений от файлов операционной системы. Проще использовать несколько файлов, каждый из которых настраивает определённый параметр, чем традиционный единый файл [.filename]#xorg.conf#. Эти файлы хранятся в подкаталоге [.filename]#/usr/local/etc/X11/xorg.conf.d/#. [[x-config-gpu]] === Графические конфигурация Прямая визуализация предоставляет возможность бесшовного использования дискретного графического процессора (dGPU) совместно с интегрированным графическим процессором (iGPU), что называется PRIME. Драйверы автоматически перекладывают ресурсоемкие задачи на dGPU, когда это требуется, и отключают его питание, когда это возможно. Чтобы запускать приложения на более мощном GPU в PRIME, используйте переменную окружения `DRI_PRIME=1`. Если несколько графических драйверов конфликтуют, драйвер графического процессора можно указать в каталоге [.filename]#/usr/local/etc/X11/xorg.conf.d/#. Для настройки драйвера Intel(R) в файле конфигурации: [[x-config-video-cards-file-intel]] .Выберите драйвер видео Intel(R) Graphics в файле [example] ==== [.filename]#/usr/local/etc/X11/xorg.conf.d/20-intel.conf# [.programlisting] .... Section "Device" Identifier "Card0" Driver "intel" EndSection .... ==== Для настройки драйвера AMD(R) в файле конфигурации: [[x-config-video-cards-file-amd]] .Выберите видеодрайвер AMD(R) Graphics в файле [example] ==== [.filename]#/usr/local/etc/X11/xorg.conf.d/20-radeon.conf# [.programlisting] .... Section "Device" Identifier "Card0" Driver "radeon" EndSection .... ==== Для настройки драйвера NVIDIA(R) в файле конфигурации: [[x-config-video-cards-file-nvidia]] .Выберите видеодрайвер NVIDIA(R) Graphics в файле [example] ==== [.filename]#/usr/local/etc/X11/xorg.conf.d/20-nvidia.conf# [.programlisting] .... Section "Device" Identifier "Card0" Driver "nvidia" EndSection .... ==== [TIP] ==== -Пакет <> также может быть использован для базового управления параметрами конфигурации, доступными в драйвере NVIDIA. +Пакет package:x11/nvidia-xconfig[] также может быть использован для базового управления параметрами конфигурации, доступными в драйвере NVIDIA. ==== Для настройки драйвера SCFB в файле конфигурации: [[x-config-video-cards-file-sfcb]] .Выберите видеодрайвер SCFB Graphics в файле [example] ==== [.filename]#/usr/local/etc/X11/xorg.conf.d/20-scfb.conf# [.programlisting] .... Section "Device" Identifier "Card0" Driver "scfb" EndSection .... ==== Для настройки драйвера VESA в файле конфигурации: [[x-config-video-cards-file-vesa]] .Выберите видеодрайвер VESA Graphics в файле [example] ==== [.filename]#/usr/local/etc/X11/xorg.conf.d/20-vesa.conf# [.programlisting] .... Section "Device" Identifier "Card0" Driver "vesa" EndSection .... ==== Для настройки нескольких графических процессоров можно добавить параметр `BusID`. Список ``ID`` шин графических процессоров можно вывести, выполнив: [source, shell] .... % pciconf -lv | grep -B3 display .... Вывод должен быть похож на следующий: [.programlisting] .... vgapci0@pci0:0:2:0: class=0x030000 rev=0x0c hdr=0x00 vendor=0x8086 device=0x46a6 subvendor=0x1028 subdevice=0x0b29 vendor = 'Intel Corporation' device = 'Alder Lake-P GT2 [Iris Xe Graphics]' class = display -- vgapci0@pci0:1:0:0: class=0x030200 rev=0xa1 hdr=0x00 vendor=0x10de device=0x25b9 subvendor=0x1028 subdevice=0x0b29 vendor = 'NVIDIA Corporation' device = 'GA107GLM [RTX A1000 Laptop GPU]' class = display .... [[x-config-video-cards-file-multiple]] .Выберите драйвер видео Intel(R) Graphics и драйвер видео NVIDIA(R) Graphics в файле [example] ==== [.filename]#/usr/local/etc/X11/xorg.conf.d/20-drivers.conf# [.programlisting] .... Section "Device" Identifier "Card0" Driver "intel" BusID "pci0:0:2:0" EndSection Section "Device" Identifier "Card1" Driver "nvidia" BusID "pci0:0:2:1" EndSection .... ==== [[x-config-monitors]] === Конфигурация монитора Почти все мониторы поддерживают стандарт Extended Display Identification Data (`EDID`). X.org использует `EDID` для взаимодействия с монитором и определения поддерживаемых разрешений и частот обновления. Затем он выбирает наиболее подходящую комбинацию настроек для работы с этим монитором. Другие разрешения, поддерживаемые монитором, можно выбрать атомарно после запуска X-сервера с помощью man:xrandr[1] или в конфигурационных файлах сервера X.org. [[x-config-monitors-xrandr]] ==== Использование RandR (Изменение размера и поворот) Выполните `man:xrandr[1]` в сессии X без параметров, чтобы увидеть список видеовыходов и обнаруженных режимов монитора: [source, shell] .... % xrandr .... Вывод должен быть похож на следующий: [.programlisting] .... Screen 0: minimum 320 x 200, current 2560 x 960, maximum 8192 x 8192 LVDS-1 connected 1280x800+0+0 (normal left inverted right x axis y axis) 261mm x 163mm - 1280x800 59.99*+ 59.81 59.91 50.00 - 1280x720 59.86 59.74 - 1024x768 60.00 - 1024x576 59.90 59.82 - 960x540 59.63 59.82 - 800x600 60.32 56.25 - 864x486 59.92 59.57 - 640x480 59.94 - 720x405 59.51 58.99 - 640x360 59.84 59.32 + 1280x800 59.99*+ 59.81 59.91 50.00 + 1280x720 59.86 59.74 + 1024x768 60.00 + 1024x576 59.90 59.82 + 960x540 59.63 59.82 + 800x600 60.32 56.25 + 864x486 59.92 59.57 + 640x480 59.94 + 720x405 59.51 58.99 + 640x360 59.84 59.32 VGA-1 connected primary 1280x960+1280+0 (normal left inverted right x axis y axis) 410mm x 257mm - 1280x1024 75.02 60.02 - 1440x900 74.98 60.07 - 1280x960 60.00* - 1280x800 74.93 59.81 - 1152x864 75.00 - 1024x768 75.03 70.07 60.00 - 832x624 74.55 - 800x600 72.19 75.00 60.32 56.25 - 640x480 75.00 72.81 66.67 59.94 - 720x400 70.08 + 1280x1024 75.02 60.02 + 1440x900 74.98 60.07 + 1280x960 60.00* + 1280x800 74.93 59.81 + 1152x864 75.00 + 1024x768 75.03 70.07 60.00 + 832x624 74.55 + 800x600 72.19 75.00 60.32 56.25 + 640x480 75.00 72.81 66.67 59.94 + 720x400 70.08 HDMI-1 disconnected (normal left inverted right x axis y axis) DP-1 disconnected (normal left inverted right x axis y axis) HDMI-2 disconnected (normal left inverted right x axis y axis) DP-2 disconnected (normal left inverted right x axis y axis) DP-3 disconnected (normal left inverted right x axis y axis) .... Это показывает, что выход `VGA-1` используется для отображения экрана с разрешением 1280x960 пикселей и частотой обновления около 60 Гц. Выход `LVDS-1` используется как дополнительный монитор с разрешением 1280x800 пикселей и частотой обновления около 60 Гц. Мониторы не подключены к разъёмам `HDMI-1`, `HDMI-2`, `DP-1`, `DP-2` и `DP-3`. Любой из других режимов отображения можно выбрать с помощью man:xrandr[1]. Например, для переключения на 1280x1024 при 60 Гц: [source, shell] .... % xrandr --output LVDS-1 --mode 1280x720 --rate 60 .... [TIP] ==== Часто черный экран при запуске X можно исправить, добавив шаг `xrandr --auto` в процесс инициализации. ==== [[x-config-monitors-file]] ==== Использование файлов конфигурации X.org Конфигурацию монитора также можно задать в файле конфигурации. Установить разрешение экрана 1024x768 в конфигурационном файле: .Установка разрешения экрана в файле [example] ==== [.filename]#/usr/local/etc/X11/xorg.conf.d/10-monitor.conf# [.programlisting] .... Section "Screen" Identifier "Screen0" Device "Card0" SubSection "Display" Modes "1024x768" EndSubSection EndSection .... ==== [[x-config-input]] === Конфигурация устройств ввода Сервер Xorg[X.org] предоставляет библиотеку package:x11/libinput[], которая представляет собой кроссплатформенное решение для поддержки всех сенсорных, указывающих и клавиатурных устройств в рамках единой библиотеки. Если не указано иное, она загружается автоматически. Индивидуальные настройки устройств для man:libinput[4] можно настроить в графическом интерфейсе crossref:desktop[desktop-synopsis,рабочего стола] или вручную с помощью package:x11/xinput[xinput] и package:x11/setxkbmap[setxkbmap]. В качестве альтернативы, в пакете package:x11-drivers[] категории x11/xf86-input-[foo] доступны более старые, легковесные отдельные драйверы для конкретных устройств ввода. Этот подход требует ручной настройки сервера X.org. Оба метода описаны в данном подразделе. [[x-config-input-atmoic]] ==== Использование файла конфигурации атомарного ввода Устройства, поддерживаемые man:libinput[4], могут быть настроены с помощью графических утилит, включенных в crossref:desktop[desktop-synopsis,рабочую среду], или вручную и атомарно во время выполнения с помощью package:x11/xinput[] и package:x11/setxkbmap[]. Чтобы узнать, к каким устройствам в данный момент подключён man:libinput[4], запустите man:xinput[1] без аргументов: [source, shell] .... $ xinput .... Её вывод должен быть похож на следующий: [example] -.... +==== ⎡ Virtual core pointer id=2 [master pointer (3)] ⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)] ⎜ ↳ System mouse id=7 [slave pointer (2)] ⎜ ↳ VEN_0488:00 0488:1031 Mouse id=11 [slave pointer (2)] ⎜ ↳ VEN_0488:00 0488:1031 TouchPad id=12 [slave pointer (2)] ⎣ Virtual core keyboard id=3 [master keyboard (2)] ↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)] ↳ System keyboard multiplexer id=6 [slave keyboard (3)] ↳ Power Button id=8 [slave keyboard (3)] ↳ Sleep Button id=9 [slave keyboard (3)] ↳ AT keyboard id=10 [slave keyboard (3)] -.... +==== Все поддерживаемые этими устройствами настройки предоставляются в виде свойств, которые можно выводить и устанавливать атомарно. Указывающие устройства имеют множество настраиваемых свойств, клавиатурам обычно не требуется ничего. Для настройки клавиатуры обратитесь к man:setxkbmap[1]. Удовлетворившись своей конфигурацией, просто добавьте строки в скрипт инициализации X, например, в [.filename]#~/.Xsession# или [.filename]#~/.xinitrc#. [[x-config-input-file]] ==== Использование файлов конфигурации X.org [TIP] ==== Некоторые среды рабочего стола (например, crossref:desktop[kde-environment,KDE Plasma]) предоставляют графический интерфейс для настройки этих параметров. Проверьте, доступен ли такой вариант, прежде чем приступать к ручному редактированию конфигурации. ==== [[x-config-input-keyboard-layout]] Например, чтобы в ручную настроить у сервера X.org раскладку клавиатуры: .Установка раскладки клавиатуры [example] ==== [.filename]#/usr/local/etc/X11/xorg.conf.d/00-keyboard.conf# [.programlisting] .... Section "InputClass" Identifier "Keyboard1" MatchIsKeyboard "on" Option "XkbLayout" "es, fr" Option "XkbModel" "pc104" Option "XkbVariant" ",qwerty" Option "XkbOptions" "grp:win_space_toggle" EndSection .... ==== [[x-fonts]] == Использование шрифтов в системе X Window _**аннотация**: Дополнительные шрифты можно установить из категории package:x11-fonts[] или разместить в [.filename]#~/.fonts#. Они становятся доступны немедленно для современных приложений. Также доступна и описана конфигурация для старых приложений._ Система X Window предоставляет библиотеку интерфейса X FreeType (man:Xft[3]) для отображения векторных или контурных шрифтов, а также традиционную систему X Logical Font Description, сохраняющую совместимость с поколениями приложений и шрифтов. Пользователей в основном интересуют два типа шрифтов: * Шрифты OpenType или TrueType(R) предназначены для отображения на экране. * Шрифты Adobe(R) PostScript(R) Type 1 предназначены для печати на бумаге. Это векторные или контурные шрифты, также существуют растровые шрифты. Коллекция портов FreeBSD включает в себя широкий и постоянно растущий каталог бесплатных высококачественных шрифтов, доступных для установки в категории package:x11-fonts[]. Системные пакеты шрифтов, установленные из коллекции портов, находятся в `[.filename]#/usr/local/share/fonts/#`. Шрифты для отдельного пользователя можно разместить в `[.filename]#~/.fonts/#` или `[.filename]#~/.local/share/fonts/#`. Шрифты в каталоге или подкаталогах станут доступны для немедленного использования после перестроения кэша информации о шрифтах. Для принудительного запуска этого процесса выполните: [source, shell] .... % fc-cache .... В дереве портов доступно множество бесплатных высококачественных шрифтов обоих типов, которые можно легко использовать с X Window System. В этой главе представлен краткий обзор обоих типов, а также настройка интерфейса X FreeType. Для получения дополнительной информации о том, как установить и настроить шрифты в FreeBSD, прочитайте статью link:{fonts}[Шрифты и FreeBSD]. [[truetype]] === Шрифты TrueType(R) В X.org есть встроенная поддержка отображения шрифтов TrueType(R). Для этого доступны два различных модуля. В данном примере используется модуль freetype, так как он более согласован с другими механизмами отображения шрифтов. Чтобы включить модуль freetype, добавьте следующую строку в раздел `"Module"` файла [.filename]#/usr/local/etc/X11/xorg.conf.d/90-fonts.conf#. [.programlisting] .... Load "freetype" .... Теперь создайте каталог для шрифтов TrueType(R) (например, [.filename]#/usr/local/share/fonts/TrueType#) и скопируйте все шрифты TrueType(R) в этот каталог. Учтите, что шрифты TrueType(R) нельзя напрямую брать из Apple(R) Mac(R); они должны быть в формате UNIX(R)/MS-DOS(R)/Windows(R) для использования в X.org. После копирования файлов в этот каталог используйте `mkfontscale` для создания [.filename]#fonts.dir#, чтобы X-сервер знал, что новые файлы установлены. `mkfontscale` можно установить как пакет: [source, shell] .... # pkg install mkfontscale .... Затем создайте индекс файлов шрифтов X в каталоге: [source, shell] .... # cd /usr/local/share/fonts/TrueType # mkfontscale .... Теперь добавьте каталог TrueType(R) в путь шрифтов. Это делается так же, как описано в <>: [source, shell] .... % xset fp+ /usr/local/share/fonts/TrueType % xset fp rehash .... или добавьте строку `FontPath` в файл [.filename]#xorg.conf#. Теперь Gimp, LibreOffice и все другие X-приложения должны распознавать установленные шрифты TrueType®. Очень мелкие шрифты (например, текст на веб-странице с высоким разрешением) и очень крупные шрифты (в LibreOffice) теперь будут выглядеть значительно лучше. [[type1]] === Шрифты Type1 Коллекция шрифтов URW (package:x11-fonts/urwfonts[]) включает высококачественные версии стандартных шрифтов type1 (Times Roman(TM), Helvetica(TM), Palatino(TM) и другие). Коллекция Freefonts (package:x11-fonts/freefonts[]) содержит гораздо больше шрифтов, но большинство из них предназначены для использования в графических программах, таких как Gimp, и не являются достаточно полными для использования в качестве экранных шрифтов. Для установки указанных коллекций шрифтов Type1 из бинарных пакетов выполните следующие команды: [source, shell] .... # pkg install urwfonts .... Аналогично с коллекцией freefont или другими. Чтобы X-сервер, сконфигурированный в ручную) обнаружил эти шрифты, добавьте соответствующую строку в файл конфигурации X-сервера ([.filename]#/usr/local/etc/X11/xorg.conf.d/90-fonts.conf#), которая выглядит следующим образом: [.programlisting] .... Section "Files" FontPath "/usr/local/share/fonts/urwfonts/" EndSection .... Или в командной строке в сеансе X выполните: [source, shell] .... % xset fp+ /usr/local/share/fonts/urwfonts % xset fp rehash .... Это будет работать, но изменения пропадут после завершения сеанса X, если не добавить их в файл автозагрузки ([.filename]#~/.xinitrc# для обычного сеанса `startx` или [.filename]#~/.xsession# при входе через графический менеджер входа, например XDM). Третий способ — использовать новый файл [.filename]#/usr/local/etc/fonts/local.conf#, как показано в <>. [[antialias]] === Анти-алиасинг шрифтов Все шрифты в X.org, находящиеся в [.filename]#/usr/local/share/fonts/# и [.filename]#~/.fonts/#, автоматически становятся доступными для сглаживания приложениям, поддерживающим Xft. Большинство современных приложений поддерживают Xft, включая KDE, GNOME и Firefox. Для управления тем, какие шрифты сглаживаются, или для настройки свойств сглаживания создайте (или отредактируйте, если он уже существует) файл [.filename]#/usr/local/etc/fonts/local.conf#. С помощью этого файла можно настроить несколько расширенных возможностей системы шрифтов Xft; в этом разделе описаны лишь некоторые простые варианты. Подробнее см. в man:fonts-conf[5]. Этот файл должен быть в формате XML. Внимательно следите за регистром символов и убедитесь, что все теги правильно закрыты. Файл начинается со стандартного заголовка XML, за которым следует определение DOCTYPE, а затем тег ``: [.programlisting] .... .... Как уже упоминалось, все шрифты в [.filename]#/usr/local/share/fonts/#, а также в [.filename]#~/.fonts/# уже доступны для приложений, поддерживающих Xft. Чтобы добавить другой каталог вне этих двух деревьев каталогов, добавьте строку следующего вида в [.filename]#/usr/local/etc/fonts/local.conf#: [.programlisting] .... /path/to/my/fonts .... После добавления новых шрифтов, особенно новых каталогов со шрифтами, перестройте кэши шрифтов: [source, shell] .... # fc-cache -f .... Антиалиасинг делает границы слегка размытыми, что улучшает читаемость очень мелкого текста и убирает "лесенки" у крупного текста, но может вызывать усталость глаз, если применяется к обычному тексту. Чтобы исключить из антиалиасинга шрифты размером меньше 14 пунктов, добавьте следующие строки: [.programlisting] .... 14 false 14 false .... Интервал для некоторых моноширинных шрифтов также может быть некорректным при сглаживании. Это особенно актуально для KDE. Одно из возможных решений — принудительно установить интервал для таких шрифтов в значение 100. Добавьте следующие строки: [.programlisting] .... fixed mono console mono .... (это создаёт псевдонимы для других распространённых названий моноширинных шрифтов как `"mono"`), а затем добавляем: [.programlisting] .... mono 100 .... Некоторые шрифты, такие как Helvetica, могут иметь проблемы при сглаживании. Обычно это проявляется в виде шрифта, который кажется разрезанным пополам по вертикали. В худшем случае это может привести к сбою приложений. Чтобы избежать этого, рекомендуется добавить следующее в [.filename]#local.conf#: [.programlisting] .... Helvetica sans-serif .... После редактирования [.filename]#local.conf# убедитесь, что файл завершается тегом ``. Если этого не сделать, изменения будут проигнорированы. Пользователи могут добавить индивидуальные настройки, создав собственный файл [.filename]#~/.config/fontconfig/fonts.conf#. Этот файл использует тот же формат `XML`, который описан выше. Последний момент: при использовании ЖК-экрана может потребоваться субпиксельная выборка. По сути, это раздельная обработка красного, зелёного и синего компонентов (разделённых горизонтально) для улучшения горизонтального разрешения; результат может быть впечатляющим. Чтобы включить эту функцию, добавьте следующую строку в файл [.filename]#local.conf#: [.programlisting] .... unknown rgb .... [NOTE] ==== В зависимости от типа дисплея, возможно, потребуется изменить `rgb` на `bgr`, `vrgb` или `vbgr`: поэкспериментируйте и выберите наиболее подходящий вариант. ==== diff --git a/documentation/content/ru/books/porters-handbook/makefiles/_index.adoc b/documentation/content/ru/books/porters-handbook/makefiles/_index.adoc index 6b1383c7ed..9c4b4f867a 100644 --- a/documentation/content/ru/books/porters-handbook/makefiles/_index.adoc +++ b/documentation/content/ru/books/porters-handbook/makefiles/_index.adoc @@ -1,4923 +1,4923 @@ --- description: 'Настройка Makefile для портов FreeBSD' next: books/porters-handbook/special params: path: /books/porters-handbook/makefiles/ prev: books/porters-handbook/slow-porting showBookMenu: 'true' tags: ["makefiles", "configuring", "naming", "versions"] title: 'Глава 5. Настройка Makefile' weight: 5 --- [[makefiles]] = Настройка Makefile :doctype: book :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumoffset: 5 :partnums: :source-highlighter: rouge :experimental: :g-plus-plus: g++ :images-path: books/porters-handbook/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] Настройка [.filename]#Makefile# довольно проста, и мы снова рекомендуем изучить существующие примеры перед началом. Также в этом руководстве есть crossref:porting-samplem[porting-samplem,пример Makefile], поэтому ознакомьтесь с ним и, пожалуйста, соблюдайте порядок переменных и разделов в этом шаблоне, чтобы порт был удобнее для чтения другими. Рассмотрите эти проблемы последовательно при разработке нового [.filename]#Makefile#: [[makefile-source]] == Оригинальный исходный код Находится ли он в `DISTDIR` в виде стандартного архива ``gzip`` с именем вроде [.filename]#foozolix-1.2.tar.gz#? Если да, переходите к следующему шагу. Если нет, возможно, для формата имени файла дистрибутива потребуется переопределить одну или несколько переменных: `DISTVERSION`, `DISTNAME`, `EXTRACT_CMD`, `EXTRACT_BEFORE_ARGS`, `EXTRACT_AFTER_ARGS`, `EXTRACT_SUFX` или `DISTFILES`. В худшем случае создайте пользовательскую цель `do-extract`, чтобы переопределить стандартную. Это редко, если вообще когда-либо, необходимо. [[makefile-naming]] == Именование Первая часть [.filename]#Makefile# порта указывает его название, описывает номер версии и помещает его в соответствующую категорию. [[makefile-portname]] === `PORTNAME` Установите `PORTNAME` как базовое имя программы. Оно используется в качестве основы для пакета FreeBSD и для crossref:makefiles[makefile-distname,`DISTNAME`]. [IMPORTANT] ==== Название пакета должно быть уникальным во всём дереве портов. Убедитесь, что `PORTNAME` ещё не используется существующим портом и что никакой другой порт уже не имеет такой же `PKGBASE`. Если имя уже занято, добавьте либо crossref:makefiles[porting-pkgnameprefix-suffix,`PKGNAMEPREFIX`, либо `PKGNAMESUFFIX`]. ==== [[makefile-versions]] === Версии, `DISTVERSION` _или_ `PORTVERSION` Установите `DISTVERSION` в номер версии программы. `PORTVERSION` — это версия, используемая для пакета FreeBSD. Она будет автоматически вычислена из `DISTVERSION` в соответствии со схемой версионирования пакетов FreeBSD. Если версия содержит _буквы_, может потребоваться задать `PORTVERSION` вместо `DISTVERSION`. [IMPORTANT] ==== Только одна из переменных `PORTVERSION` и `DISTVERSION` может быть установлена одновременно. ==== Время от времени некоторые программы используют схему версионирования, которая несовместима с тем, как `DISTVERSION` преобразуется в `PORTVERSION`. [TIP] ==== При обновлении порта можно использовать аргумент `-t` утилиты man:pkg-version[8], чтобы проверить, является ли новая версия больше или меньше предыдущей. Смотрите ниже, как использовать man:pkg-version[8] для сравнения версий. ==== [[makefile-versions-ex-pkg-version]] .Использование man:pkg-version[8] для сравнения версий [example] ==== `pkg version -t` принимает две версии в качестве аргументов и возвращает `<`, `=` или `>`, если первая версия меньше, равна или больше второй версии соответственно. [source, shell] .... % pkg version -t 1.2 1.3 < <.> % pkg version -t 1.2 1.2 = <.> % pkg version -t 1.2 1.2.0 = <.> % pkg version -t 1.2 1.2.p1 > <.> % pkg version -t 1.2.a1 1.2.b1 < <.> % pkg version -t 1.2 1.2p1 < <.> .... <.> `1.2` идёт перед `1.3`. <.> `1.2` и `1.2` равны, так как имеют одинаковую версию. <.> `1.2` и `1.2.0` равны, так как ноль ничего не значит. <.> `1.2` идёт после `1.2.p1`, так как `.p1` означает «pre-release 1» (предрелизная версия 1). <.> `1.2.a1` предшествует `1.2.b1`, представьте "alpha" и "beta", где `a` идёт перед `b`. <.> `1.2` находится перед `1.2p1`, так же как `2p1` (читается как "2, уровень исправления 1") — это версия, следующая после любой `2.X`, но перед `3`. [NOTE] -**** +===== Здесь `a`, `b` и `p` используются так, как если бы они означали "альфа", "бета" или "пре-релиз" и "уровень патча", но на самом деле это просто буквы, которые сортируются в алфавитном порядке, поэтому можно использовать любую букву, и они будут отсортированы соответствующим образом. -**** +===== ==== .Примеры `DISTVERSION` и производной `PORTVERSION` [cols="10%,90%", frame="none", options="header"] |=== | DISTVERSION | .PORTVERSION |0.7.1d |0.7.1.d |10Alpha3 |10.a3 |3Beta7-pre2 |3.b7.p2 |8:f_17 |8f.17 |=== [[makefile-versions-ex1]] .Использование `DISTVERSION` [example] ==== Если версия содержит только числа, разделённые точками, тире или подчёркиваниями, используйте `DISTVERSION`. [.programlisting] .... PORTNAME= nekoto DISTVERSION= 1.2-4 .... Это сгенерирует `PORTVERSION` равный `1.2.4`. ==== [[makefile-versions-ex2]] .Использование `DISTVERSION` когда версия начинается с буквы или префикса [example] ==== Когда версия начинается или заканчивается буквой, или префиксом, или суффиксом, которые не являются частью версии, используйте `DISTVERSIONPREFIX`, `DISTVERSION` и `DISTVERSIONSUFFIX`. Если версия `v1.2-4`: [.programlisting] .... PORTNAME= nekoto DISTVERSIONPREFIX= v DISTVERSION= 1_2_4 .... Некоторые проекты, использующие GitHub, могут включать своё название в версии. Например, версия может выглядеть как `nekoto-1.2-4`: [.programlisting] .... PORTNAME= nekoto DISTVERSIONPREFIX= nekoto- DISTVERSION= 1.2_4 .... Эти проекты также иногда используют строку в конце версии, например, `1.2-4_RELEASE`: [.programlisting] .... PORTNAME= nekoto DISTVERSION= 1.2-4 DISTVERSIONSUFFIX= _RELEASE .... Или они делают и то, и другое, например, `nekoto-1.2-4_RELEASE`: [.programlisting] .... PORTNAME= nekoto DISTVERSIONPREFIX= nekoto- DISTVERSION= 1.2-4 DISTVERSIONSUFFIX= _RELEASE .... `DISTVERSIONPREFIX` и `DISTVERSIONSUFFIX` не будут использоваться при формировании `PORTVERSION`, а только в `DISTNAME`. Все сгенерируют `PORTVERSION` равный `1.2.4`. ==== [[makefile-versions-ex3]] .Использование `DISTVERSION`, когда версия содержит буквы, означающие "alpha", "beta" или "pre-release" [example] ==== Если версия содержит числа, разделённые точками, тире или подчёркиваниями, а буквы используются для обозначения "альфа", "бета" или "предварительной версии" (то есть до версии без букв), используйте `DISTVERSION`. [.programlisting] .... PORTNAME= nekoto DISTVERSION= 1.2-pre4 .... [.programlisting] .... PORTNAME= nekoto DISTVERSION= 1.2p4 .... Оба варианта создадут `PORTVERSION` равную `1.2.p4`, что предшествует версии 1.2. Для проверки этого факта можно использовать man:pkg-version[8]: [source, shell] .... % pkg version -t 1.2.p4 1.2 < .... ==== [[makefile-versions-ex4]] .Не использовать `DISTVERSION`, если версия содержит буквы, означающие "уровень патча" [example] ==== Если версия содержит буквы, которые не означают "alpha", "beta" или "pre", а скорее указывают на "уровень исправления" и следуют после версии без букв, используйте `PORTVERSION`. [.programlisting] .... PORTNAME= nekoto PORTVERSION= 1.2p4 .... В данном случае использование `DISTVERSION` невозможно, так как это приведёт к генерации версии `1.2.p4`, которая будет считаться более ранней, чем `1.2`, а не более поздней. man:pkg-version[8] подтвердит это: [source, shell] .... % pkg version -t 1.2 1.2.p4 > <.> % pkg version -t 1.2 1.2p4 < <.> .... <.> `1.2` идёт после `1.2.p4`, что в данном случае _неверно_. <.> `1.2` находится перед `1.2p4`, что и требовалось. ==== Для более сложных примеров настройки `PORTVERSION`, когда версия программного обеспечения действительно несовместима с FreeBSD, или `DISTNAME`, когда файл дистрибутива не содержит саму версию, см. crossref:makefiles[makefile-distname, `DISTNAME`]. [[makefile-naming-revepoch]] === `PORTREVISION` и `PORTEPOCH` [[makefile-portrevision]] ==== `PORTREVISION` `PORTREVISION` — это монотонно возрастающее значение, которое сбрасывается в 0 при каждом увеличении `DISTVERSION`, обычно при каждом новом официальном выпуске от поставщика. Если `PORTREVISION` не равен нулю, его значение добавляется к имени пакета. Изменения `PORTREVISION` используются автоматизированными инструментами, такими как man:pkg-version[8], для определения доступности нового пакета. `PORTREVISION` должен быть увеличен каждый раз, когда в порт вносятся изменения, которые так или иначе влияют на сгенерированный пакет. Это включает изменения, затрагивающие только пакеты, собранные с нестандартными crossref:makefiles[makefile-options,опциями]. Примеры случаев, когда необходимо увеличить `PORTREVISION`: * Добавление исправлений для устранения уязвимостей безопасности, ошибок или для добавления новой функциональности в порт. * Изменения в [.filename]#Makefile# порта для включения или отключения параметров сборки в пакете. * Изменения в списке файлов пакета или в поведении во время установки. Например, изменение скрипта, который генерирует начальные данные для пакета, такие как ключи хоста man:ssh[1]. * Увеличение версии зависимости порта от общей библиотеки (в данном случае, попытка установить старый пакет после установки более новой версии зависимости завершится неудачей, так как будет искаться старая версия libfoo.x вместо libfoo.(x+1)). * Тихие изменения в дистрибутивном файле порта, которые имеют значительные функциональные отличия. Например, изменения в дистрибутивном файле, требующие корректировки файла [.filename]#distinfo# без соответствующего изменения `DISTVERSION`, когда сравнение `diff -ru` старой и новой версий показывает нетривиальные изменения в коде. * Изменения в `MAINTAINER`. Примеры изменений, которые не требуют увеличения `PORTREVISION`: * Стилевые изменения в каркасе портов без функциональных изменений в итоговом пакете. * Изменения в `MASTER_SITES` или другие функциональные изменения порта, которые не влияют на итоговый пакет. * Тривиальные исправления в дистрибутивном файле, такие как исправление опечаток, которые не настолько важны, чтобы пользователи пакета были вынуждены тратить время на обновление. * Исправления сборки, которые позволяют пакету компилироваться там, где ранее это не удавалось. При условии, что изменения не вносят функциональных изменений на других платформах, где порт ранее собирался. Поскольку `PORTREVISION` отражает содержимое пакета, если пакет ранее не мог быть собран, то нет необходимости увеличивать `PORTREVISION` для обозначения изменений. Эмпирическое правило заключается в том, чтобы решить, является ли изменение, внесённое в порт, чем-то, что принесёт пользу _некоторым_ пользователям. Будь то улучшение, исправление или просто факт, что новый пакет вообще будет работать. Затем необходимо сопоставить это с тем, что всем, кто регулярно обновляет своё дерево портов, придётся выполнить обновление. Если ответ положительный, необходимо увеличить `PORTREVISION`. [NOTE] ==== Пользователи бинарных пакетов _никогда_ не увидят обновления, если `PORTREVISION` не увеличен. Без увеличения `PORTREVISION` сборщики пакетов не могут обнаружить изменение и, следовательно, не пересоберут пакет. ==== [[makefile-portepoch]] ==== `PORTEPOCH` Время от времени разработчики программного обеспечения или сопровождающие портов FreeBSD совершают ошибку и выпускают версию своего ПО, которая фактически имеет меньший номер по сравнению с предыдущей. Примером может служить переход с foo-20000801 на foo-1.0 (первая версия будет ошибочно считаться более новой, поскольку число 20000801 больше, чем 1.0). [TIP] ==== Результаты сравнения номеров версий не всегда очевидны. Команда `pkg version` (см. man:pkg-version[8]) может быть использована для проверки сравнения двух строк с номерами версий. Например: [source, shell] .... % pkg version -t 0.031 0.29 > .... Символ `>` в выводе указывает, что версия 0.031 считается больше, чем версия 0.29, что могло быть неочевидно для сопровождающего. ==== В таких ситуациях необходимо увеличить `PORTEPOCH`. Если `PORTEPOCH` не равен нулю, он добавляется к имени пакета, как описано в разделе 0 выше. `PORTEPOCH` никогда не должен уменьшаться или сбрасываться до нуля, потому что это приведёт к ошибке при сравнении с пакетом из более ранней эпохи. Например, пакет не будет обнаружен как устаревший. Новый номер версии, `1.0,1` в приведённом выше примере, всё ещё численно меньше предыдущей версии, 20000801, но суффикс `,1` обрабатывается автоматизированными инструментами особым образом и считается большим, чем подразумеваемый суффикс `,0` у предыдущего пакета. Неправильное удаление или сброс `PORTEPOCH` приводит к бесконечным проблемам. Если приведённое выше объяснение недостаточно ясно, обратитесь к {freebsd-ports}. Ожидается, что `PORTEPOCH` не будет использоваться для большинства портов, и что разумное использование `DISTVERSION` или аккуратное применение `PORTVERSION` часто может предотвратить необходимость его использования, если будущий выпуск программного обеспечения изменит структуру версий. Однако разработчикам портов FreeBSD следует быть осторожными, когда вендор выпускает релиз без официального номера версии — например, релиз в виде "снимка" кода. Возникает соблазн обозначить такой релиз датой выпуска, что вызовет проблемы, как в примере выше, когда будет сделан новый "официальный" релиз. Например, если снимок выпущен на дату `20000917`, а предыдущая версия программного обеспечения была `1.2`, не следует использовать `20000917` для `DISTVERSION`. Правильным будет указать `DISTVERSION` как `1.2.20000917` или подобное, чтобы следующая версия, например `1.3`, оставалась численно большей. [[makefile-portrevision-example]] ==== Пример использования `PORTREVISION` и `PORTEPOCH` Порт `gtkmumble` версии `0.10` добавлен в коллекцию портов: [.programlisting] .... PORTNAME= gtkmumble DISTVERSION= 0.10 .... `PKGNAME` становится `gtkmumble-0.10`. Обнаружена уязвимость в безопасности, требующая локального исправления FreeBSD. `PORTREVISION` соответствующим образом увеличивается. [.programlisting] .... PORTNAME= gtkmumble DISTVERSION= 0.10 PORTREVISION= 1 .... `PKGNAME` принимает значение `gtkmumble-0.10_1` Выпущена новая версия от поставщика под номером `0.2` (оказывается, автор изначально подразумевал, что `0.10` на самом деле означает `0.1.0`, а не "то, что идёт после 0.9" — увы, теперь уже поздно). Поскольку новая минорная версия `2` численно меньше предыдущей версии `10`, необходимо увеличить `PORTEPOCH`, чтобы вручную заставить систему распознавать новый пакет как "более новый". Так как это новый релиз кода от поставщика, `PORTREVISION` сбрасывается до 0 (или удаляется из [.filename]#Makefile#). [.programlisting] .... PORTNAME= gtkmumble DISTVERSION= 0.2 PORTEPOCH= 1 .... `PKGNAME` становится `gtkmumble-0.2,1` Следующий выпуск — 0.3. Поскольку `PORTEPOCH` никогда не уменьшается, переменные версий теперь выглядят так: [.programlisting] .... PORTNAME= gtkmumble DISTVERSION= 0.3 PORTEPOCH= 1 .... `PKGNAME` принимает значение `gtkmumble-0.3,1` [NOTE] ==== Если бы `PORTEPOCH` был сброшен в `0` при этом обновлении, пользователь, установивший пакет `gtkmumble-0.10_1`, не увидел бы пакет `gtkmumble-0.3` как более новый, поскольку `3` всё ещё численно меньше, чем `10`. Помните, в этом и заключается вся суть `PORTEPOCH` изначально. ==== [[porting-pkgnameprefix-suffix]] === `PKGNAMEPREFIX` и `PKGNAMESUFFIX` Две необязательные переменные, `PKGNAMEPREFIX` и `PKGNAMESUFFIX`, объединяются с `PORTNAME` и `PORTVERSION` для формирования `PKGNAME` в виде `${PKGNAMEPREFIX}${PORTNAME}${PKGNAMESUFFIX}-${PORTVERSION}`. Убедитесь, что это соответствует нашим crossref:makefiles[porting-pkgname,рекомендациям по именованию пакетов]. В частности, использование дефиса (`-`) в `PORTVERSION` _не_ допускается. Кроме того, если имя пакета содержит часть _language-_ или _-compiled.specifics_ (см. ниже), используйте `PKGNAMEPREFIX` и `PKGNAMESUFFIX` соответственно. Не включайте их в `PORTNAME`. [[porting-pkgname]] === Соглашения о наименовании пакетов Вот соглашения, которым следует придерживаться при наименовании пакетов. Это сделано для того, чтобы каталог пакетов было легко просматривать, поскольку там уже тысячи пакетов, и пользователи могут отказаться от их использования, если это будет напрягать их глаза! Имена пакетов имеют формат [.filename]#language_region-name-compiled.specifics-version.numbers#. Имя пакета определяется как `${PKGNAMEPREFIX}${PORTNAME}${PKGNAMESUFFIX}-${PORTVERSION}`. Убедитесь, что переменные заданы в соответствии с этим форматом. [[porting-pkgname-language]] [.filename]#language_region-#:: FreeBSD стремится поддерживать родной язык своих пользователей. Часть _language-_ представляет собой двухбуквенное сокращение естественного языка, определённое стандартом ISO-639, когда порт относится к определённому языку. Примерами являются `ja` для японского, `ru` для русского, `vi` для вьетнамского, `zh` для китайского, `ko` для корейского и `de` для немецкого. + Если порт относится к определённому региону в языковой зоне, добавьте также двухбуквенный код страны. Например, `en_US` для американского английского и `fr_CH` для швейцарского французского. + Часть _language-_ задается в `PKGNAMEPREFIX`. [[porting-pkgname-name]] [.filename]#name#:: Убедитесь, что название порта и его версия четко разделены и указаны в `PORTNAME` и `DISTVERSION`. Единственная причина, по которой `PORTNAME` может содержать часть версии, — это если вышестоящее распространяемое ПО действительно так названо, как в портах package:textproc/libxml2[] или package:japanese/kinput2-freewnn[]. В противном случае `PORTNAME` не может содержать информацию о версии. Довольно нормально, когда несколько портов имеют одинаковый `PORTNAME`, как это делают порты package:www/apache*[]; в таком случае разные версии (и разные записи в индексе) различаются значениями `PKGNAMEPREFIX` и `PKGNAMESUFFIX`. + Существует традиция называть модули `Perl 5`, добавляя префикс `p5-` и заменяя разделитель в виде двойного двоеточия на дефис. Например, модуль `Data::Dumper` становится `p5-Data-Dumper`. [[porting-pkgname-compiled-specifics]] [.filename]#-compiled.specifics#:: Если порт может быть собран с различными crossref:makefiles[makefile-masterdir,жёстко заданными значениями по умолчанию] (обычно это часть имени каталога в семействе портов), часть _-compiled.specifics_ указывает скомпилированные значения по умолчанию. Дефис является необязательным. Примерами могут служить размер бумаги и единицы измерения шрифтов. + Часть _-compiled.specifics_ задаётся в `PKGNAMESUFFIX`. [[porting-pkgname-version-numbers]] [.filename]#-version.numbers#:: Строка версии следует после тире (`-`) и представляет собой разделённый точками список целых чисел и строчных букв латинского алфавита. В частности, не допускается использование дополнительных тире внутри строки версии. Единственное исключение — строка `pl` (означающая "уровень исправления"), которую можно использовать _только_ в случае отсутствия у программного обеспечения номеров основной и дополнительной версий. Если в версии программного обеспечения встречаются строки типа "alpha", "beta", "rc" или "pre", следует взять первую букву и поместить её сразу после точки. Если после таких названий строка версии продолжается, числа следуют за буквой без дополнительной точки между ними (например, `1.0b2`). + Идея заключается в упрощении сортировки портов за счёт анализа строки версии. В частности, необходимо убедиться, что компоненты номера версии всегда разделены точкой, а если дата является частью строки, использовать формат `d__yyyy.mm.dd__`, а не `_dd.mm.yyyy_` или не соответствующий стандарту Y2K формат `_yy.mm.dd_`. Важно добавлять перед версией букву, в данном случае `d` (от слова "дата"), на случай, если будет выпущена версия с фактическим номером, который численно окажется меньше `_yyyy_`. [IMPORTANT] ==== Название пакета должно быть уникальным среди всех портов в дереве. Убедитесь, что порт с таким же `PORTNAME` ещё не существует, и если он есть, добавьте один из crossref:makefiles[porting-pkgnameprefix-suffix,`PKGNAMEPREFIX` или `PKGNAMESUFFIX`]. ==== Вот несколько (реальных) примеров преобразования названия, указанного авторами программного обеспечения, в подходящее имя пакета. В каждой строке указана только одна из переменных `DISTVERSION` или `PORTVERSION`, в зависимости от того, какая используется в [.filename]#Makefile# порта: .Примеры наименования пакетов [cols="1,1,1,1,1,1,1", frame="none", options="header"] |=== | Имя дистрибутива | PKGNAMEPREFIX | PORTNAME | PKGNAMESUFFIX | DISTVERSION | .PORTVERSION | Причина или комментарий |mule-2.2.2 |(пусто) |mule |(пусто) |2.2.2 -| +| |Никаких изменений не требуется |mule-1.0.1 |(пусто) |mule |1 |1.0.1 -| +| |Это версия 1 mule, а версия 2 уже существует |EmiClock-1.0.2 |(пусто) |emiclock |(пусто) |1.0.2 -| +| |Нет имён в верхнем регистре для отдельных программ |rdist-1.3alpha |(пусто) |rdist |(пусто) |1.3alpha -| +| |Версия будет `1.3.a` |es-0.9-beta1 |(пусто) |es |(пусто) |0.9-beta1 -| +| |Версия будет `0.9.b1` |mailman-2.0rc3 |(пусто) |mailman |(пусто) |2.0rc3 -| +| |Версия будет `2.0.r3` |v3.3beta021.src |(пусто) |tiff |(пусто) -| +| |3.3 |Что это вообще было? |tvtwm |(пусто) |tvtwm |(пусто) -| +| |p11 |Нет версии в имени файла, используйте то, что указано в исходном коде |piewm |(пусто) |piewm |(пусто) |1.0 -| +| |Нет версии в имени файла, используйте то, что указано в исходном коде |xvgr-2.10pl1 |(пусто) |xvgr |(пусто) -| +| |2.10.pl1 |В таком случае, `pl1` означает уровень патча, поэтому использование DISTVERSION невозможно. |gawk-2.15.6 |ja- |gawk |(пусто) |2.15.6 -| +| |Японская языковая версия |psutils-1.13 |(пусто) |psutils |-letter |1.13 -| +| |Размер бумаги жёстко задан во время сборки пакета |pkfonts |(пусто) |pkfonts |300 |1.0 -| +| |Пакет для шрифтов с разрешением 300dpi |=== Если в исходном источнике полностью отсутствует информация о версии и маловероятно, что автор когда-либо выпустит новую версию, просто укажите строку версии как `1.0` (как в примере с `piewm` выше). В противном случае, спросите автора или используйте дату выпуска исходного файла в формате `d__yyyy.mm.dd__` или `d__yyyymmdd__` в качестве версии. [TIP] ==== Используйте любую букву. Здесь `d` означает дату, если источник — это репозиторий Git, часто используется `g` с последующей датой коммита, также распространено использование `s` для снимка. ==== [[makefile-categories]] == Категоризация [[makefile-categories-definition]] === `CATEGORIES` При создании пакета он помещается в [.filename]#/usr/ports/packages/All#, и ссылки на него создаются в одном или нескольких подкаталогах [.filename]#/usr/ports/packages#. Имена этих подкаталогов задаются переменной `CATEGORIES`. Это предназначено для облегчения поиска пакетов пользователем при просмотре большого количества пакетов на FTP-сайте или CDROM. Пожалуйста, ознакомьтесь с crossref:makefiles[porting-categories,текущим списком категорий] и выберите подходящие для данного порта. Этот список также определяет, где в дереве портов будет размещён порт. Если здесь указано несколько категорий, файлы порта должны быть помещены в подкаталог с названием первой категории. Дополнительные сведения о выборе подходящих категорий см. в crossref:makefiles[choosing-categories,ниже]. [[porting-categories]] === Текущий список категорий Вот текущий список категорий портов. Категории, помеченные звёздочкой (`*`), являются _виртуальными_ — они не имеют соответствующего подкаталога в дереве портов. Они используются только как вторичные категории и исключительно для целей поиска. [NOTE] ==== Для невиртуальных категорий в `COMMENT` в [.filename]#Makefile# соответствующего подкаталога содержится однострочное описание. ==== [.informaltable] [cols="1,1,1", frame="none", options="header"] |=== | Категория | Описание | Заметки |[.filename]#accessibility# |Порты для помощи пользователям с ограниченными возможностями. -| +| |[.filename]#afterstep#`*` |Порты для поддержки оконного менеджера http://www.afterstep.org/[AfterStep]. -| +| |[.filename]#arabic# |Поддержка арабского языка. -| +| |[.filename]#archivers# |Инструменты для архивирования. -| +| |[.filename]#astro# |Астрономические порты. -| +| |[.filename]#audio# |Поддержка звука. -| +| |[.filename]#benchmarks# |Утилиты для тестирования производительности. -| +| |[.filename]#biology# |Программное обеспечение, связанное с биологией. -| +| |[.filename]#budgie#`*` |Программное обеспечение, связанное со средой рабочего стола Budgie. -| +| |[.filename]#cad# |Компьютерные средства автоматизированного проектирования. -| +| |[.filename]#chinese# |Поддержка китайского языка. -| +| |[.filename]#comms# |Программное обеспечение для связи. |В основном программное обеспечение для работы с последовательным портом. |[.filename]#converters# |Преобразователи символьных кодировок. -| +| |[.filename]#databases# |Базы данных. -| +| |[.filename]#deskutils# |Вещи, которые раньше находились на рабочем столе до изобретения компьютеров. -| +| |[.filename]#devel# |Средства разработки. |Не размещайте библиотеки здесь только потому, что они являются библиотеками. Они _не_ должны быть в этой категории, если только они действительно не подходят никуда больше. |[.filename]#dns# |Программное обеспечение, связанное с DNS. -| +| |[.filename]#docs#`*` |Мета-порты для документации FreeBSD. -| +| |[.filename]#editors# |Общие редакторы. |Специализированные редакторы помещаются в раздел соответствующих инструментов. Например, редактор математических формул будет помещён в [.filename]#math#, а [.filename]#editors# будет для него второй категорией. |[.filename]#education#`*` |Программное обеспечение для образования. |Это включает приложения, утилиты или игры, разработанные в первую очередь или в значительной степени для помощи пользователю в изучении конкретной темы или обучении в целом. Также сюда входят приложения для создания курсов, приложения для предоставления курсов и приложения для управления классом или школой |[.filename]#elisp#`*` |Порты Emacs-lisp. -| +| |[.filename]#emulators# |Эмуляторы других операционных систем. |Терминальные эмуляторы _не_ относятся сюда. Основанные на X идут в [.filename]#x11#, а текстовые — либо в [.filename]#comms#, либо в [.filename]#misc#, в зависимости от конкретной функциональности. |[.filename]#enlightenment#`*` |Порты, связанные с оконным менеджером Enlightenment. -| +| |[.filename]#filesystems# |Файловые системы и связанные утилиты. -| +| |[.filename]#finance# |Монетарные, финансовые и связанные с ними приложения. -| +| |[.filename]#french# |Поддержка французского языка. -| +| |[.filename]#ftp# |Клиентские и серверные утилиты FTP. |Если порт поддерживает как FTP, так и HTTP, поместите его в [.filename]#ftp# с дополнительной категорией [.filename]#www#. |[.filename]#games# |Игры. -| +| |[.filename]#география#`*` |Программное обеспечение, связанное с географией. -| +| |[.filename]#german# |Поддержка немецкого языка. -| +| |[.filename]#gnome#`*` |Порты из проекта https://www.gnome.org/[GNOME]. -| +| |[.filename]#gnustep#`*` |Программное обеспечение, связанное со средой рабочего стола GNUstep. -| +| |[.filename]#graphics# |Графические утилиты. -| +| |[.filename]#hamradio#`*` |Программное обеспечение для радиолюбителей. -| +| |[.filename]#haskell#`*` |Программное обеспечение, связанное с языком Haskell. -| +| |[.filename]#hebrew# |Поддержка иврита. -| +| |[.filename]#hungarian# |Венгерская языковая поддержка. -| +| |[.filename]#irc# |Утилиты Internet Relay Chat. -| +| |[.filename]#japanese# |Поддержка японского языка. -| +| |[.filename]#java# |Программное обеспечение, связанное с языком Java(TM). |Категория [.filename]#java# не должна быть единственной для порта. За исключением портов, непосредственно связанных с языком Java, разработчикам также рекомендуется не использовать [.filename]#java# в качестве основной категории для порта. |[.filename]#kde#`*` |Порты проекта https://www.kde.org/[KDE] (общие). -| +| |[.filename]#kde-приложения#`*` |Приложения от проекта https://www.kde.org/[KDE]. -| +| |[.filename]#kde-frameworks#`*` |Дополнительные библиотеки от проекта https://www.kde.org/[KDE] для программирования с использованием Qt. -| +| |[.filename]#kde-plasma#`*` |Рабочий стол от проекта https://www.kde.org/[KDE]. -| +| |[.filename]#kld#`*` |Загружаемые модули ядра. -| +| |[.filename]#korean# |Поддержка корейского языка. -| +| |[.filename]#lang# |Языки программирования. -| +| |[.filename]#linux#`*` |Приложения и вспомогательные утилиты Linux. -| +| |[.filename]#lisp#`*` |Программное обеспечение, связанное с языком Lisp. -| +| |[.filename]#mail# |Почтовое программное обеспечение. -| +| |[.filename]#mate#`*` |Порты, связанные с окружением рабочего стола MATE, форком GNOME 2. -| +| |[.filename]#math# |Численные расчеты и другие математические утилиты. -| +| |[.filename]#mbone#`*` |Приложения MBone. -| +| |[.filename]#misc# |Различные утилиты |Вещи, которые не подходят никуда больше. По возможности, попытайтесь найти для порта категорию лучше, чем `misc`, так как порты здесь часто остаются без внимания. |[.filename]#multimedia# |Мультимедийное программное обеспечение. -| +| |[.filename]#net# |Различное сетевое программное обеспечение. -| +| |[.filename]#net-im# |Программное обеспечение для обмена мгновенными сообщениями. -| +| |[.filename]#net-mgmt# |Программное обеспечение для управления сетями. -| +| |[.filename]#net-p2p# |Одноранговые сетевые приложения. -| +| |[.filename]#сеть-vpn#`*` |Виртуальные частные сети. -| +| |[.filename]#news# |Программное обеспечение для USENET-новостей. -| +| |[.filename]#parallel#`*` |Приложения, работающие с параллелизмом в вычислениях. -| +| |[.filename]#pear#`*` |Порты, связанные с PHP-фреймворком Pear. -| +| |[.filename]#perl5#`*` |Порты, требующие Perl версии 5 для работы. -| +| |[.filename]#plan9#`*` |Различные программы с https://9p.io/wiki/plan9/Download/index.html[Plan9]. -| +| |[.filename]#polish# |Поддержка польского языка. -| +| |[.filename]#ports-mgmt# |Порты для управления, установки и разработки портов и пакетов FreeBSD. -| +| |[.filename]#portuguese# |Поддержка португальского языка. -| +| |[.filename]#print# |Программное обеспечение для печати. |Инструменты для настольных издательских систем (превьюеры и т. д.) также относятся сюда. |[.filename]#python#`*` |Программное обеспечение, связанное с языком https://www.python.org/[Python]. -| +| |[.filename]#ruby#`*` |Программное обеспечение, связанное с языком https://www.ruby-lang.org/[Ruby]. -| +| |[.filename]#rubygems#`*` |Порты пакетов https://www.rubygems.org/[RubyGems]. -| +| |[.filename]#russian# |Поддержка русского языка. -| +| |[.filename]#scheme#`*` |Программное обеспечение, связанное с языком Scheme. -| +| |[.filename]#science# |Научные порты, которые не входят в другие категории, такие как [.filename]#astro#, [.filename]#biology# и [.filename]#math#. -| +| |[.filename]#security# |Средства обеспечения безопасности. -| +| |[.filename]#shells# |Командные оболочки. -| +| |[.filename]#spanish#`*` |Поддержка испанского языка. -| +| |[.filename]#sysutils# |Системные утилиты. -| +| |[.filename]#tcl#`*` |Порты, использующие Tcl для запуска. -| +| |[.filename]#textproc# |Средства обработки текста. |Он не включает инструменты для настольных издательских систем, которые помещаются в [.filename]#print#. |[.filename]#tk#`*` |Порты, использующие Tk для работы. -| +| |[.filename]#ukrainian# |Поддержка украинского языка. -| +| |[.filename]#vietnamese# |Поддержка вьетнамского языка. -| +| |[.filename]#wayland#`*` |Порты для поддержки сервера дисплея Wayland. -| +| |[.filename]#windowmaker#`*` |Порты для поддержки оконного менеджера Window Maker. -| +| |[.filename]#www# |Программное обеспечение, связанное с Всемирной паутиной. |Поддержка языка HTML также относится сюда. |[.filename]#x11# |Система X Window и связанные компоненты. |Эта категория предназначена только для программного обеспечения, которое напрямую поддерживает оконную систему. Не помещайте сюда обычные X-приложения. Большинство из них относятся к другим категориям [.filename]#x11-*# (см. ниже). |[.filename]#x11-clocks# |Часы X11. -| +| |[.filename]#x11-drivers# |Драйверы X11. -| +| |[.filename]#x11-fm# |Менеджеры файлов X11. -| +| |[.filename]#x11-fonts# |Шрифты и утилиты для работы со шрифтами в X11. -| +| |[.filename]#x11-servers# |Серверы X11. -| +| |[.filename]#x11-themes# |Темы X11. -| +| |[.filename]#x11-toolkits# |Инструментарии X11. -| +| |[.filename]#x11-wm# |Оконные менеджеры X11. -| +| |[.filename]#xfce#`*` |Порты, связанные с окружением рабочего стола https://www.xfce.org/[Xfce]. -| +| |[.filename]#zope#`*` |https://www.zope.org/[Zope] поддержка. -| +| |=== [[choosing-categories]] === Выбор подходящей категории Поскольку многие категории пересекаются, выбор основной категории для порта может быть утомительным. Существует несколько правил, регулирующих этот вопрос. Вот список приоритетов в порядке убывания важности: * Первая категория должна быть физической (см. crossref:makefiles[porting-categories,выше]). Это необходимо для работы упаковки. Виртуальные категории и физические категории могут чередоваться после этого. * Языковые категории всегда указываются первыми. Например, если порт устанавливает японские шрифты для X11, то строка `CATEGORIES` будет выглядеть так: [.filename]#japanese x11-fonts#. * Конкретные категории перечислены перед менее специфичными. Например, HTML-редактор указывается как [.filename]#www editors#, а не наоборот. Также не следует указывать [.filename]#net#, если порт принадлежит к любой из категорий [.filename]#irc#, [.filename]#mail#, [.filename]#news#, [.filename]#security# или [.filename]#www#, так как [.filename]#net# подразумевается автоматически. * [.filename]#x11# используется как вторичная категория только в случае, когда основной категорией указан естественный язык. В частности, не указывайте [.filename]#x11# в строке категории для X-приложений. * Режимы Emacs размещаются в той же категории портов, что и приложение, поддерживаемое данным режимом, а не в [.filename]#editors#. Например, режим Emacs для редактирования исходных файлов какого-либо языка программирования попадает в [.filename]#lang#. * Порты, устанавливающие загружаемые модули ядра, также имеют виртуальную категорию [.filename]#kld# в строке `CATEGORIES`. Это одна из вещей, автоматически обрабатываемых при добавлении `USES=kmod`. * [.filename]#misc# не встречается вместе с другими невиртуальными категориями. Если `misc` указан вместе с чем-то ещё в `CATEGORIES`, это означает, что `misc` можно безопасно удалить, а порт разместить только в другом подкаталоге. * Если порт действительно не подходит никуда больше, поместите его в [.filename]#misc#. Если категория не определена четко, пожалуйста, укажите это в комментарии при https://bugs.freebsd.org/submit/[отправке порта] в баг-трекере, чтобы мы могли обсудить её перед импортом. Как коммиттер, отправьте сообщение в рассылку {freebsd-ports}, чтобы мы сначала обсудили это. Слишком часто новые порты импортируются в неправильную категорию, после чего их сразу же приходится перемещать. [[proposing-categories]] === Предложение новой категории По мере роста Коллекции портов со временем были введены различные новые категории. Новые категории могут быть _виртуальными_ — те, у которых нет соответствующего подкаталога в дереве портов, или _физическими_ — те, у которых он есть. В этом разделе обсуждаются вопросы, связанные с созданием новой физической категории. Внимательно ознакомьтесь с ним, прежде чем предлагать новую. Наша текущая практика заключается в том, чтобы избегать создания новой физической категории, если только либо большое количество портов логически принадлежит к ней, либо порты, которые к ней относятся, представляют собой логически обособленную группу, представляющую ограниченный общий интерес (например, категории, связанные с разговорными человеческими языками), или, желательно, оба условия одновременно. Обоснование этого заключается в том, что такое изменение создаёт extref:{committers-guide}[значительный объём работы, ports] как для коммиттеров, так и для всех пользователей, которые отслеживают изменения в Коллекции портов. Кроме того, предлагаемые изменения категорий, как правило, вызывают споры. (Возможно, это связано с отсутствием четкого консенсуса относительно того, когда категория становится «слишком большой», а также относительно того, должны ли категории способствовать удобству просмотра (и, следовательно, какое количество категорий было бы идеальным), и так далее.) Вот процедура: [.procedure] . Предложите новую категорию на {freebsd-ports}. Включите подробное обоснование для новой категории, объясните, почему существующие категории недостаточны, и укажите список существующих портов, предлагаемых к перемещению. (Если в Bugzilla есть ожидающие рассмотрения новые порты, которые подходят под эту категорию, также перечислите их.) Если вы являетесь сопровождающим или подающим предложение, укажите это, так как это может помочь в рассмотрении. . Участвуйте в обсуждении. . Если кажется, что идея находит поддержку, оформите PR, включающий как обоснование, так и список существующих портов, которые необходимо переместить. В идеале, этот PR также должен содержать следующие исправления: ** [.filename]##Makefile## для новых портов после копирования их репозитория ** [.filename]#Makefile# для новой категории ** [.filename]#Makefile# для старых категорий портов ** [.filename]##Makefile## для портов, зависящих от старых портов ** (для дополнительной оценки включите другие файлы, которые необходимо изменить, в соответствии с процедурой, описанной в Руководстве коммиттера.) . Поскольку это затрагивает инфраструктуру портов и включает перемещение и исправление многих портов, а также, возможно, проведение регрессионных тестов на сборочном кластере, назначьте PR для {portmgr}. . Если этот PR будет одобрен, коммиттер должен будет выполнить оставшуюся часть процедуры, extref:{committers-guide}[описанной в Руководстве коммиттера,ports]. Предложение новой виртуальной категории аналогично описанному выше, но гораздо менее трудоёмко, так как фактически не потребуется перемещать порты. В этом случае единственные патчи, которые нужно включить в PR, — это добавление новой категории в `CATEGORIES` затронутых портов. [[proposing-reorg]] === Предложение о реорганизации всех категорий Изредка кто-то предлагает реорганизовать категории, используя либо двухуровневую структуру, либо какую-либо другую структуру ключевых слов. На сегодняшний день ни одно из этих предложений не было реализовано, потому что, хотя их очень легко выдвинуть, усилия, необходимые для переработки всей существующей коллекции портов в рамках любой реорганизации, пугают, мягко говоря. Пожалуйста, ознакомьтесь с историей этих предложений в архивах списка рассылки, прежде чем публиковать эту идею. Более того, будьте готовы к тому, что вас попросят предоставить рабочий прототип. [[makefile-distfiles]] == Файлы дистрибутива Вторая часть [.filename]#Makefile# описывает файлы, которые необходимо загрузить для сборки порта, и места, откуда их можно скачать. [[makefile-distname]] === `DISTNAME` `DISTNAME` — это имя порта, используемое авторами программного обеспечения. По умолчанию `DISTNAME` имеет значение `${PORTNAME}-${DISTVERSIONPREFIX}${DISTVERSION}${DISTVERSIONSUFFIX}`, а если не задано, `DISTVERSION` по умолчанию принимает значение `${PORTVERSION}`, поэтому переопределяйте `DISTNAME` только при необходимости. `DISTNAME` используется только в двух случаях. Во-первых, список файлов дистрибутива (`DISTFILES`) по умолчанию имеет значение `${DISTNAME}${EXTRACT_SUFX}`. Во-вторых, ожидается, что файл дистрибутива распакуется в подкаталог с именем `WRKSRC`, который по умолчанию равен [.filename]#work/${DISTNAME}#. Некоторые названия дистрибутивов от поставщиков, которые не соответствуют схеме `${PORTNAME}-${PORTVERSION}`, могут обрабатываться автоматически путем установки `DISTVERSIONPREFIX`, `DISTVERSION` и `DISTVERSIONSUFFIX`. `PORTVERSION` будет автоматически вычисляться из `DISTVERSION`. [IMPORTANT] ==== Только одна из переменных `PORTVERSION` и `DISTVERSION` может быть установлена одновременно. Если `DISTVERSION` не определяет корректную `PORTVERSION`, не используйте `DISTVERSION`. ==== Если схема версий исходного проекта может быть преобразована в схему, совместимую с портами, установите некоторую переменную в версию исходного проекта, _не используйте_ имя переменной `DISTVERSION`. Установите `PORTVERSION` в вычисленную версию на основе созданной переменной и задайте `DISTNAME` соответствующим образом. Если схема версионирования вышестоящего проекта не может быть легко преобразована в значение, совместимое с портами, установите `PORTVERSION` в разумное значение и задайте `DISTNAME` как `PORTNAME` с дословной версией вышестоящего проекта. [[makefile-distname-ex1]] .Получение `PORTVERSION` вручную [example] ==== BIND9 использует схему версионирования, несовместимую с версиями портов (в версиях используется `-`), и её нельзя получить с помощью `DISTVERSION`, так как после выпуска 9.9.9 выходят «уровни исправлений» в формате `9.9.9-P1`. `DISTVERSION` преобразует это в `9.9.9.p1`, что в схеме версионирования портов означает 9.9.9 pre-release 1, то есть версию, предшествующую 9.9.9, а не следующую за ней. Поэтому `PORTVERSION` вручную формируется из переменной `ISCVERSION`, чтобы получить `9.9.9p1`. Порядок, в котором система портов и pkg будут сортировать версии, проверяется с помощью аргумента `-t` из man:pkg-version[8]: [source, shell] .... % pkg version -t 9.9.9 9.9.9.p1 > <.> % pkg version -t 9.9.9 9.9.9p1 < <.> .... <.> Знак `>` означает, что первый аргумент, переданный в `-t`, больше второго аргумента. `9.9.9` находится после `9.9.9.p1`. <.> Знак `<` означает, что первый аргумент, переданный в `-t`, меньше второго аргумента. `9.9.9` находится перед `9.9.9p1`. В файле [.filename]#Makefile# порта, например package:dns/bind99[], это достигается с помощью: [.programlisting] .... PORTNAME= bind PORTVERSION= ${ISCVERSION:S/-P/P/:S/b/.b/:S/a/.a/:S/rc/.rc/} CATEGORIES= dns net MASTER_SITES= ISC/bind9/${ISCVERSION} PKGNAMESUFFIX= 99 DISTNAME= ${PORTNAME}-${ISCVERSION} MAINTAINER= mat@FreeBSD.org COMMENT= BIND DNS suite with updated DNSSEC and DNS64 WWW= https://www.isc.org/bind/ LICENSE= ISCL # ISC releases things like 9.8.0-P1 or 9.8.1rc1, which our versioning does not like ISCVERSION= 9.9.9-P6 .... Определите версию вышестоящего пакета в `ISCVERSION`, с комментарием, объясняющим, _почему_ это необходимо. Используйте `ISCVERSION` для получения совместимого с портами `PORTVERSION`. Используйте `ISCVERSION` напрямую для получения правильного URL для загрузки файла дистрибутива. Используйте `ISCVERSION` напрямую для именования дистрибутивного файла. ==== [[makefile-distname-ex2]] .Получить `DISTNAME` из `PORTVERSION` [example] ==== Время от времени имя файла дистрибутива имеет мало отношения или вообще никакого отношения к версии программного обеспечения. В пакете package:comms/kermit[], в файле дистрибутива присутствует только последний элемент версии: [.programlisting] .... PORTNAME= kermit PORTVERSION= 9.0.304 CATEGORIES= comms ftp net MASTER_SITES= ftp://ftp.kermitproject.org/kermit/test/tar/ DISTNAME= cku${PORTVERSION:E}-dev20 .... Модификатор `:E` man:make[1] возвращает суффикс переменной, в данном случае `304`. Файл дистрибутива корректно создаётся как `cku304-dev20.tar.gz`. ==== [[makefile-distname-ex3]] .Экзотический случай 1 [example] ==== Иногда нет связи между названием программы, её версией и файлом дистрибутива, в котором она распространяется. Из пакета package:audio/libworkman[]: [.programlisting] .... PORTNAME= libworkman PORTVERSION= 1.4 CATEGORIES= audio MASTER_SITES= LOCAL/jim DISTNAME= ${PORTNAME}-1999-06-20 .... ==== [[makefile-distname-ex4]] .Экзотический случай 2 [example] ==== В пакете package:comms/librs232[] файл дистрибутива не имеет версии, поэтому необходимо использовать crossref:makefiles[makefile-dist_subdir,`DIST_SUBDIR`]: [.programlisting] .... PORTNAME= librs232 PORTVERSION= 20160710 CATEGORIES= comms MASTER_SITES= http://www.teuniz.net/RS-232/ DISTNAME= RS-232 DIST_SUBDIR= ${PORTNAME}-${PORTVERSION} .... ==== [NOTE] ==== `PKGNAMEPREFIX` и `PKGNAMESUFFIX` не влияют на `DISTNAME`. Также обратите внимание, что если `WRKSRC` равно [.filename]#${WRKDIR}/${DISTNAME}#, а исходный архив с исходным кодом называется иначе, чем `${PORTNAME}-${PORTVERSION}${EXTRACT_SUFX}`, оставьте `DISTNAME` без изменений — определение только `DISTFILES` проще, чем определение и `DISTNAME`, и `WRKSRC` (а возможно, и `EXTRACT_SUFX`). ==== [[makefile-master_sites]] === `MASTER_SITES` Запишите именем каталога из FTP/HTTP-URL, указывающего на исходный tarball, в `MASTER_SITES`. Не забудьте завершающий слэш ([.filename]#/#)! Макросы `make` будут пытаться использовать эту спецификацию для загрузки файла дистрибутива с помощью `FETCH`, если не смогут найти его уже в системе. Рекомендуется включать в этот список несколько сайтов, желательно с разных континентов. Это обеспечит защиту от проблем в глобальной сети. [IMPORTANT] ==== `MASTER_SITES` не должен быть пустым. Он должен указывать на реальный сайт, где размещены файлы дистрибутива. Он не может указывать на веб-архивы или кэшированные сайты с файлами дистрибутива FreeBSD. Единственное исключение из этого правила — порты, у которых нет файлов дистрибутива. Например, мета-порты не имеют файлов дистрибутива, поэтому `MASTER_SITES` не нужно задавать. ==== [[makefile-master_sites-shorthand]] ==== Использование переменных `MASTER_SITE_*` Для популярных архивов, таких как SourceForge (`SOURCEFORGE`), GNU (`GNU`) или Perl CPAN (`PERL_CPAN`), доступны сокращённые обозначения. `MASTER_SITES` может использовать их напрямую: [.programlisting] .... MASTER_SITES= GNU/make .... Старый расширенный формат по-прежнему работает, но все порты были преобразованы в компактный формат. Расширенный формат выглядит следующим образом: [.programlisting] .... MASTER_SITES= ${MASTER_SITE_GNU} MASTER_SITE_SUBDIR= make .... Эти значения и переменные определены в https://cgit.freebsd.org/ports/tree/Mk/bsd.sites.mk[Mk/bsd.sites.mk]. Новые записи добавляются часто, поэтому обязательно проверяйте последнюю версию этого файла перед отправкой порта. [TIP] ==== Для любой переменной `MASTER_SITE_FOO` можно использовать сокращение `_FOO_`. Например, используйте: [.programlisting] .... MASTER_SITES= FOO .... Если требуется `MASTER_SITE_SUBDIR`, используйте следующее: [.programlisting] .... MASTER_SITES= FOO/bar .... ==== [NOTE] ==== Некоторые имена `MASTER_SITE_*` довольно длинные, и для удобства использования были определены сокращения: [[makefile-master_sites-shortcut]] .Сокращения для макросов `MASTER_SITE_*` [cols="1,1", frame="none", options="header"] |=== | Макрос | Сокращение |`PERL_CPAN` |`CPAN` |`GITHUB` |`GH` |`GITHUB_CLOUD` |`GHC` |`LIBREOFFICE_DEV` |`LODEV` |`NETLIB` |`NL` |`RUBYGEMS` |`RG` |`SOURCEFORGE` |`SF` |=== ==== [[makefile-master_sites-magic]] ==== Волшебные макросы MASTER_SITES Существует несколько "волшебных" макросов для популярных сайтов с предсказуемой структурой каталогов. Для них достаточно использовать сокращение, и система автоматически выберет подкаталог. Например, для порта с именем `Stardict`, версии `1.2.3`, размещенного на SourceForge, добавьте следующую строку: [.programlisting] .... MASTER_SITES= SF .... подразумевает подкаталог с именем `/project/stardict/stardict/1.2.3`. Если подразумеваемый каталог указан неверно, его можно переопределить: [.programlisting] .... MASTER_SITES= SF/stardict/WyabdcRealPeopleTTS/${PORTVERSION} .... Это также можно записать как [.programlisting] .... MASTER_SITES= SF MASTER_SITE_SUBDIR= stardict/WyabdcRealPeopleTTS/${PORTVERSION} .... [[makefile-master_sites-popular]] .Волшебные макросы `MASTER_SITES` [cols="1,1", frame="none", options="header"] |=== | Макрос | Предполагаемый подкаталог |`APACHE_COMMONS_BINARIES` |`${PORTNAME:S,commons-,,}` |`APACHE_COMMONS_SOURCE` |`${PORTNAME:S,commons-,,}` |`APACHE_JAKARTA` |`${PORTNAME:S,-,/,}/source` |`BERLIOS` |`${PORTNAME:tl}.berlios` |`PYPI` |`source/${DISTNAME:C/(.).\*/\1/}/${DISTNAME:C/(.*)-[0-9].*/\1/}` |`CPAN` |`${PORTNAME:C/-.*//}` |`DEBIAN` |`pool/main/${PORTNAME:C/^((lib)?.).*$/\1/}/${PORTNAME}` |`FARSIGHT` |`${PORTNAME}` |`FESTIVAL` |`${PORTREVISION}` |`GCC` |`releases/${DISTNAME}` |`GENTOO` |`distfiles` |`GIMP` |`${PORTNAME}/${PORTVERSION:R}/` |`GH` |`${GH_ACCOUNT}/${GH_PROJECT}/tar.gz/${GH_TAGNAME}?dummy=/` |`GHC` |`${GH_ACCOUNT}/${GH_PROJECT}/` |`GNOME` |`sources/${PORTNAME}/${PORTVERSION:C/^([0-9]+\.[0-9]+).*/\1/}` |`GNU` |`${PORTNAME}` |`GNUPG` |`${PORTNAME}` |`GNU_ALPHA` |`${PORTNAME}` |`HORDE` |`${PORTNAME}` |`LODEV` |`${PORTNAME}` |`MATE` |`${PORTVERSION:C/^([0-9]+\.[0-9]+).*/\1/}` |`MOZDEV` |`${PORTNAME:tl}` |`NL` |`${PORTNAME}` |`QT` |`archive/qt/${PORTVERSION:R}` |`SAMBA` |`${PORTNAME}` |`SAVANNAH` |`${PORTNAME:tl}` |`SF` |`${PORTNAME:tl}/${PORTNAME:tl}/${PORTVERSION}` |=== [[makefile-master_sites-github]] === `USE_GITHUB` Если файл дистрибутива получен из определённого коммита или тега на https://github.com/[GitHub], для которого нет официально выпущенного файла, существует простой способ автоматически установить правильные значения `DISTNAME` и `MASTER_SITES`. [WARNING] ==== По состоянию на 2023-02-21 link:https://github.blog/2023-02-21-update-on-the-future-stability-of-source-code-archives-and-hashes/[GitHub] объявили, что загрузки исходного кода будут стабильными в течение года. Пожалуйста, переключитесь на ресурсы выпусков (release assets), а если они недоступны, запросите их создание у вышестоящих разработчиков. ==== Доступны следующие переменные: [[makefile-master_sites-github-description]] .`USE_GITHUB` Описание [cols="1,1,1", options="header"] |=== | Переменная | Описание | По умолчанию |`GH_ACCOUNT` |Имя учётной записи пользователя GitHub, который размещает проект |`${PORTNAME}` |`GH_PROJECT` |Название проекта на GitHub |`${PORTNAME}` |`GH_TAGNAME` |Имя тега для загрузки (2.0.1, хэш, ...) Использование имени ветки здесь некорректно. Также можно использовать хэш идентификатора коммита для создания снимка состояния. |`${DISTVERSIONPREFIX}${DISTVERSION}${DISTVERSIONSUFFIX}` |`GH_SUBDIR` |Когда программному обеспечению требуется дополнительный файл дистрибутива для извлечения в `${WRKSRC}`, можно использовать эту переменную. Примеры можно найти в crossref:makefiles[makefile-master_sites-github-multiple, Загрузка нескольких файлов из GitHub] для получения дополнительной информации. |(отсутствует) |`GH_TUPLE` |`GH_TUPLE` позволяет объединить `GH_ACCOUNT`, `GH_PROJECT`, `GH_TAGNAME` и `GH_SUBDIR` в одну переменную. Формат следующий: _account_`:`_project_`:`_tagname_`:`_group_`/`_subdir_. Часть `/`_subdir_ является необязательной. Это полезно, когда требуется получить несколько проектов с GitHub. -| +| |=== [IMPORTANT] ==== Не используйте `GH_TUPLE` для файла дистрибутива по умолчанию, так как у него нет значения по умолчанию. ==== [[makefile-master_sites-github-ex1]] .Простое использование `USE_GITHUB` [example] ==== При попытке создать порт для версии `1.2.7` pkg от пользователя FreeBSD на github, по адресу https://github.com/freebsd/pkg/[], файл [.filename]#Makefile# в итоге будет выглядеть следующим образом (незначительно сокращено для примера): [.programlisting] .... PORTNAME= pkg DISTVERSION= 1.2.7 USE_GITHUB= yes GH_ACCOUNT= freebsd .... Он автоматически получит `MASTER_SITES` установленным в `GH` и `WRKSRC` в `${WRKDIR}/pkg-1.2.7`. ==== [[makefile-master_sites-github-ex2]] .Более полное использование `USE_GITHUB` [example] ==== При попытке создать порт для самой последней версии pkg от пользователя FreeBSD на github, по адресу https://github.com/freebsd/pkg/[], файл [.filename]#Makefile# в итоге выглядит следующим образом (незначительно сокращено для примера): [.programlisting] .... PORTNAME= pkg-devel DISTVERSION= 1.3.0.a.20140411 USE_GITHUB= yes GH_ACCOUNT= freebsd GH_PROJECT= pkg GH_TAGNAME= 6dbb17b .... Он автоматически получит `MASTER_SITES` со значением `GH` и `WRKSRC` со значением `${WRKDIR}/pkg-6dbb17b`. [TIP] -**** +==== `20140411` — это дата коммита, указанного в `GH_TAGNAME`, а не дата редактирования файла [.filename]#Makefile# или дата создания коммита. -**** +==== ==== [[makefile-master_sites-github-ex3]] .Использование `USE_GITHUB` с `DISTVERSIONPREFIX` [example] ==== Время от времени `GH_TAGNAME` немного отличается от `DISTVERSION`. Например, если версия `1.0.2`, то тег будет `v1.0.2`. В таких случаях можно использовать `DISTVERSIONPREFIX` или `DISTVERSIONSUFFIX`: [.programlisting] .... PORTNAME= foo DISTVERSIONPREFIX= v DISTVERSION= 1.0.2 USE_GITHUB= yes .... Он автоматически установит `GH_TAGNAME` в `v1.0.2`, в то время как `WRKSRC` останется `${WRKDIR}/foo-1.0.2`. ==== [[makefile-master_sites-github-ex4]] .Использование `USE_GITHUB` при отсутствии версий у исходного проекта [example] ==== Если никогда не было версии вышестоящего репозитория, не изобретайте её, например `0.1` или `1.0`. Создайте порт с `DISTVERSION` в формате `g__YYYYMMDD__`, где `g` означает Git, а `_YYYYMMDD_` представляет дату коммита, указанного в `GH_TAGNAME`. [.programlisting] .... PORTNAME= bar DISTVERSION= g20140411 USE_GITHUB= yes GH_TAGNAME= c472d66b .... Это создаёт схему версионирования, которая увеличивается со временем и всё ещё находится до версии `0`. Подробности об использовании man:pkg-version[8] для сравнения версий смотрите в crossref:makefiles[makefile-versions-ex-pkg-version, этой секции]: [source, shell] .... % pkg version -t g20140411 0 < .... Что означает, что использование `PORTEPOCH` не потребуется, если вышестоящий проект решит сократить версии в будущем. ==== [[makefile-master_sites-github-ex5]] .Использование `USE_GITHUB` для доступа к коммиту между двумя версиями [example] ==== Если текущая версия программного обеспечения использует тег Git, и порт необходимо обновить до более новой промежуточной версии без тега, используйте man:git-describe[1], чтобы определить версию для использования: [source, shell] .... % git describe --tags f0038b1 v0.7.3-14-gf0038b1 .... `v0.7.3-14-gf0038b1` можно разделить на три части: `v0.7.3`:: Это последний тег Git, который появляется в истории коммитов перед запрошенным коммитом. `-14`:: Это означает, что запрошенный коммит `f0038b1` является 14-м коммитом после тега `v0.7.3`. `-gf0038b1`:: `-g` означает "Git", а `f0038b1` — это хеш коммита, на который указывает данная ссылка. [.programlisting] .... PORTNAME= bar DISTVERSIONPREFIX= v DISTVERSION= 0.7.3-14 DISTVERSIONSUFFIX= -gf0038b1 USE_GITHUB= yes .... Это создаёт схему версионирования, которая увеличивается со временем (точнее, с коммитами) и не конфликтует с созданием версии `0.7.4`. Подробности об использовании man:pkg-version[8] для сравнения версий смотрите в crossref:makefiles[makefile-versions-ex-pkg-version, этой секции] : [source, shell] .... % pkg version -t 0.7.3 0.7.3.14 < % pkg version -t 0.7.3.14 0.7.4 < .... [NOTE] -**** +===== Если запрошенный коммит совпадает с тегом, по умолчанию отображается более короткое описание. Полная версия эквивалентна: [source, shell] .... % git describe --tags c66c71d v0.7.3 % git describe --tags --long c66c71d v0.7.3-0-gc66c71d .... -**** +===== ==== [[makefile-master_sites-github-multiple]] ==== Извлечение нескольких файлов из GitHub Фреймворк `USE_GITHUB` также поддерживает загрузку нескольких файлов дистрибутива из разных мест в GitHub. Он работает очень похоже на crossref:makefiles[porting-master-sites-n, Файлы дистрибуции или патчей из нескольких мест]. В `GH_ACCOUNT`, `GH_PROJECT` и `GH_TAGNAME` добавляются несколько значений. Каждому различному значению присваивается группа. Основное значение может не иметь группы или принадлежать группе `:DEFAULT`. Значение может быть опущено, если оно совпадает со значением по умолчанию, указанным в crossref:makefiles[makefile-master_sites-github-description,описании `USE_GITHUB`]. `GH_TUPLE` также можно использовать, когда имеется множество файлов дистрибутива. Это помогает сохранять учётные данные, проект, имя тега и информацию о группе в одном месте. Для каждой группы создаётся вспомогательная переменная `${WRKSRC_group}`, содержащая каталог, в который был извлечён файл. Переменные `${WRKSRC_group}` могут использоваться для перемещения каталогов во время `post-extract`, добавления в `CONFIGURE_ARGS` или любых других действий, необходимых для корректной сборки программного обеспечения. [CAUTION] ==== Часть `:__group__` _должна_ использоваться _только для одного_ файла дистрибутива. Она служит уникальным ключом, и её повторное использование приведёт к перезаписи предыдущих значений. ==== [NOTE] ==== Поскольку это всего лишь синтаксический сахар над `DISTFILES` и `MASTER_SITES`, имена групп должны соответствовать ограничениям на имена групп, описанным в crossref:makefiles[porting-master-sites-n, Файлы дистрибутивов или патчей из нескольких источников] ==== При получении нескольких файлов из GitHub иногда файл дистрибутива по умолчанию не загружается из GitHub. Чтобы отключить загрузку файла дистрибутива по умолчанию, установите: [.programlisting] .... USE_GITHUB= nodefault .... [IMPORTANT] ==== При использовании `USE_GITHUB=nodefault` в [.filename]#Makefile# необходимо указать `DISTFILES` в его crossref:porting-order[porting-order-portname,верхнем блоке]. Определение должно быть следующим: [.programlisting] .... DISTFILES= ${DISTNAME}${EXTRACT_SUFX} .... ==== [[makefile-master_sites-github-multi]] .Использование `USE_GITHUB` с несколькими файлами дистрибутива [example] ==== Время от времени возникает необходимость загрузить более одного файла дистрибутива. Например, когда вышестоящий репозиторий git использует подмодули. Это можно легко сделать с помощью групп в переменных `GH_*`: [.programlisting] .... PORTNAME= foo DISTVERSION= 1.0.2 USE_GITHUB= yes GH_ACCOUNT= bar:icons,contrib GH_PROJECT= foo-icons:icons foo-contrib:contrib GH_TAGNAME= 1.0:icons fa579bc:contrib GH_SUBDIR= ext/icons:icons CONFIGURE_ARGS= --with-contrib=${WRKSRC_contrib} .... Это загрузит три файла дистрибутива с github. Стандартный берется из [.filename]#foo/foo# и имеет версию `1.0.2`. Второй, с группой `icons`, берется из [.filename]#bar/foo-icons# и имеет версию `1.0`. Третий берется из [.filename]#bar/foo-contrib# и использует Git-коммит `fa579bc`. Файлы дистрибутива называются [.filename]#foo-foo-1.0.2_GH0.tar.gz#, [.filename]#bar-foo-icons-1.0_GH0.tar.gz# и [.filename]#bar-foo-contrib-fa579bc_GH0.tar.gz#. Все файлы дистрибутива извлекаются в `${WRKDIR}` в соответствующих подкаталогах. Основной файл по-прежнему извлекается в `${WRKSRC}`, в данном случае, [.filename]#${WRKDIR}/foo-1.0.2#. Каждый дополнительный файл дистрибутива извлекается в `${WRKSRC_group}`. Здесь, для группы `icons`, он называется `${WRKSRC_icons}` и содержит [.filename]#${WRKDIR}/foo-icons-1.0#. Файл с группой `contrib` называется `${WRKSRC_contrib}` и содержит `${WRKDIR}/foo-contrib-fa579bc`. Система сборки программы ожидает найти иконки в подкаталоге [.filename]#ext/icons# в её исходниках, поэтому используется `GH_SUBDIR`. `GH_SUBDIR` гарантирует, что [.filename]#ext# существует, но [.filename]#ext/icons# ещё не существует. Затем он выполняет следующее: [.programlisting] .... post-extract: @${MV} ${WRKSRC_icons} ${WRKSRC}/ext/icons .... ==== [[makefile-master_sites-github-multi2]] .Использование `USE_GITHUB` с несколькими файлами дистрибутива с помощью `GH_TUPLE` [example] ==== Это функционально эквивалентно crossref:makefiles[makefile-master_sites-github-multi,Использованию `USE_GITHUB` с несколькими файлами дистрибутива], но с использованием `GH_TUPLE`: [.programlisting] .... PORTNAME= foo DISTVERSION= 1.0.2 USE_GITHUB= yes GH_TUPLE= bar:foo-icons:1.0:icons/ext/icons \ bar:foo-contrib:fa579bc:contrib CONFIGURE_ARGS= --with-contrib=${WRKSRC_contrib} .... В предыдущем примере использовалась группировка с `bar:icons,contrib`. В `GH_TUPLE` присутствует избыточная информация, так как группировка невозможна. ==== [[makefile-master_sites-github-submodules]] .Как использовать `USE_GITHUB` с подмодулями Git? [example] ==== Порты, использующие GitHub в качестве вышестоящего репозитория, иногда применяют подмодули. Подробнее см. man:git-submodule[1]. Проблема с подмодулями заключается в том, что каждый из них является отдельным репозиторием. Таким образом, каждый из них должен быть загружен отдельно. В качестве примера используем пакет package:finance/moneymanagerex[], его репозиторий на GitHub находится по адресу https://github.com/moneymanagerex/moneymanagerex/[]. В корне репозитория есть файл https://github.com/moneymanagerex/moneymanagerex/blob/master/.gitmodules[.gitmodules]. Этот файл описывает все подмодули, используемые в данном репозитории, и перечисляет дополнительные необходимые репозитории. Этот файл покажет, какие дополнительные репозитории требуются: [.programlisting] .... [submodule "lib/wxsqlite3"] path = lib/wxsqlite3 url = https://github.com/utelle/wxsqlite3.git [submodule "3rd/mongoose"] path = 3rd/mongoose url = https://github.com/cesanta/mongoose.git [submodule "3rd/LuaGlue"] path = 3rd/LuaGlue url = https://github.com/moneymanagerex/LuaGlue.git [submodule "3rd/cgitemplate"] path = 3rd/cgitemplate url = https://github.com/moneymanagerex/html-template.git [...] .... Единственная информация, отсутствующая в этом файле, — это хэш коммита или тег, который следует использовать в качестве версии. Эта информация находится после клонирования репозитория: [source, shell] .... % git clone --recurse-submodules https://github.com/moneymanagerex/moneymanagerex.git Cloning into 'moneymanagerex'... remote: Counting objects: 32387, done. [...] Submodule '3rd/LuaGlue' (https://github.com/moneymanagerex/LuaGlue.git) registered for path '3rd/LuaGlue' Submodule '3rd/cgitemplate' (https://github.com/moneymanagerex/html-template.git) registered for path '3rd/cgitemplate' Submodule '3rd/mongoose' (https://github.com/cesanta/mongoose.git) registered for path '3rd/mongoose' Submodule 'lib/wxsqlite3' (https://github.com/utelle/wxsqlite3.git) registered for path 'lib/wxsqlite3' [...] Cloning into '/home/mat/work/freebsd/ports/finance/moneymanagerex/moneymanagerex/3rd/LuaGlue'... Cloning into '/home/mat/work/freebsd/ports/finance/moneymanagerex/moneymanagerex/3rd/cgitemplate'... Cloning into '/home/mat/work/freebsd/ports/finance/moneymanagerex/moneymanagerex/3rd/mongoose'... Cloning into '/home/mat/work/freebsd/ports/finance/moneymanagerex/moneymanagerex/lib/wxsqlite3'... [...] Submodule path '3rd/LuaGlue': checked out 'c51d11a247ee4d1e9817dfa2a8da8d9e2f97ae3b' Submodule path '3rd/cgitemplate': checked out 'cd434eeeb35904ebcd3d718ba29c281a649b192c' Submodule path '3rd/mongoose': checked out '2140e5992ab9a3a9a34ce9a281abf57f00f95cda' Submodule path 'lib/wxsqlite3': checked out 'fb66eb230d8aed21dec273b38c7c054dcb7d6b51' [...] % cd moneymanagerex % git submodule status c51d11a247ee4d1e9817dfa2a8da8d9e2f97ae3b 3rd/LuaGlue (heads/master) cd434eeeb35904ebcd3d718ba29c281a649b192c 3rd/cgitemplate (cd434ee) 2140e5992ab9a3a9a34ce9a281abf57f00f95cda 3rd/mongoose (6.2-138-g2140e59) fb66eb230d8aed21dec273b38c7c054dcb7d6b51 lib/wxsqlite3 (v3.4.0) [...] .... Это также можно найти на GitHub. Каждый подкаталог, который является подмодулем, отображается как `_каталог @ хэш_`, например, `mongoose @ 2140e59`. [NOTE] -**** +===== Хотя получение информации из GitHub кажется более простым, данные, полученные с помощью `git submodule status`, будут более информативными. Например, здесь хеш коммита ``lib/wxsqlite3`` `fb66eb2` соответствует `v3.4.0`. Оба варианта можно использовать взаимозаменяемо, но если доступен тег, предпочтительнее использовать его. -**** +===== Теперь, когда вся необходимая информация собрана, можно написать [.filename]#Makefile# (показаны только строки, связанные с GitHub): [.programlisting] .... PORTNAME= moneymanagerex DISTVERSIONPREFIX= v DISTVERSION= 1.3.0 USE_GITHUB= yes GH_TUPLE= utelle:wxsqlite3:v3.4.0:wxsqlite3/lib/wxsqlite3 \ moneymanagerex:LuaGlue:c51d11a:lua_glue/3rd/LuaGlue \ moneymanagerex:html-template:cd434ee:html_template/3rd/cgitemplate \ cesanta:mongoose:2140e59:mongoose/3rd/mongoose \ [...] .... ==== [[makefile-master_sites-gitlab]] === `USE_GITLAB` Подобно GitHub, если файл дистрибутива поставляется с https://gitlab.com/[gitlab.com] или использует программное обеспечение GitLab, эти переменные доступны для использования и могут потребовать установки. [[makefile-master_sites-gitlab-description]] .Описание `USE_GITLAB` [cols="1,1,1", options="header"] |=== | Переменная | Описание | По умолчанию |`GL_SITE` |Название сайта, на котором размещен проект GitLab |https://gitlab.com/ |`GL_ACCOUNT` |Имя учётной записи пользователя GitLab, размещающего проект |`${PORTNAME}` |`GL_PROJECT` |Название проекта на GitLab |`${PORTNAME}` |`GL_COMMIT` |Хэш коммита для загрузки. Должен быть полным 160-битным, 40-символьным шестнадцатеричным хэшем sha1. Это обязательная переменная для GitLab. |`(нет)` |`GL_SUBDIR` |Когда программному обеспечению требуется дополнительный файл дистрибутива для извлечения в `${WRKSRC}`, можно использовать эту переменную. Примеры можно найти в crossref:makefiles[makefile-master_sites-gitlab-multiple, Загрузка нескольких файлов из GitLab] для получения дополнительной информации. |(отсутствует) |`GL_TUPLE` |`GL_TUPLE` позволяет объединить `GL_SITE`, `GL_ACCOUNT`, `GL_PROJECT`, `GL_COMMIT` и `GL_SUBDIR` в одну переменную. Формат записи: _сайт_`:`_учётная запись_`:`_проект_`:`_коммит_`:`_группа_`/`_подкаталог_. Части _сайт_`:` и `/`_подкаталог_ являются необязательными. Это полезно, когда требуется загрузить данные из нескольких проектов GitLab. -| +| |=== [[makefile-master_sites-gitlab-ex1]] .Простое использование `USE_GITLAB` [example] ==== Пытаясь создать порт для версии `1.14` библиотеки libsignon-glib от пользователя accounts-sso на gitlab.com, по адресу https://gitlab.com/accounts-sso/libsignon-glib/[], файл [.filename]#Makefile# будет выглядеть следующим образом для загрузки дистрибутивных файлов: [.programlisting] .... PORTNAME= libsignon-glib DISTVERSION= 1.14 USE_GITLAB= yes GL_ACCOUNT= accounts-sso GL_COMMIT= e90302e342bfd27bc8c9132ab9d0ea3d8723fd03 .... Он автоматически получит `MASTER_SITES`, установленный на https://gitlab.com/[gitlab.com], и `WRKSRC` на `${WRKDIR}/libsignon-glib-e90302e342bfd27bc8c9132ab9d0ea3d8723fd03-e90302e342bfd27bc8c9132ab9d0ea3d8723fd03`. ==== [[makefile-master_sites-gitlab-ex2]] .Более полное использование `USE_GITLAB` [example] ==== Более полный пример использования вышеописанного, если порт не имеет версионирования и foobar принадлежит пользователю foo в проекте bar на самостоятельно размещенном сайте GitLab `https://gitlab.example.com/`, тогда [.filename]#Makefile# будет выглядеть следующим образом для загрузки дистрибутивных файлов: [.programlisting] .... PORTNAME= foobar DISTVERSION= g20170906 USE_GITLAB= yes GL_SITE= https://gitlab.example.com GL_ACCOUNT= foo GL_PROJECT= bar GL_COMMIT= 9c1669ce60c3f4f5eb43df874d7314483fb3f8a6 .... В нём будет установлено `MASTER_SITES` в `"https://gitlab.example.com"` и `WRKSRC` в `${WRKDIR}/bar-9c1669ce60c3f4f5eb43df874d7314483fb3f8a6-9c1669ce60c3f4f5eb43df874d7314483fb3f8a6`. [TIP] ====== `20170906` — это дата коммита, указанного в `GL_COMMIT`, а не дата редактирования файла [.filename]#Makefile# или дата коммита в дерево портов FreeBSD. ====== [NOTE] ====== Протокол, порт и корневой каталог веб-сервера ``GL_SITE`` могут быть изменены в той же переменной. ====== ==== [[makefile-master_sites-gitlab-multiple]] ==== Извлечение нескольких файлов из GitLab Фреймворк `USE_GITLAB` также поддерживает загрузку нескольких файлов дистрибутивов из различных мест GitLab и сайтов, размещённых на GitLab. Он работает очень похоже на crossref:makefiles[porting-master-sites-n, Несколько файлов дистрибутивов или патчей из разных местоположений] и crossref:makefiles[makefile-master_sites-gitlab-multiple, Загрузка нескольких файлов из GitLab]. В `GL_SITE`, `GL_ACCOUNT`, `GL_PROJECT` и `GL_COMMIT` добавляются множественные значения. Каждое уникальное значение назначается группе. crossref:makefiles[makefile-master_sites-gitlab-description,Описание `USE_GITLAB`]. `GL_TUPLE` также может использоваться, когда имеется множество файлов дистрибутива. Это помогает хранить информацию о сайте, учётной записи, проекте, коммите и группе в одном месте. Для каждой группы создаётся вспомогательная переменная `${WRKSRC_group}`, содержащая каталог, в который был извлечён файл. Переменные `${WRKSRC_group}` могут использоваться для перемещения каталогов во время `post-extract`, добавления в `CONFIGURE_ARGS` или любых других действий, необходимых для корректной сборки программного обеспечения. [CAUTION] ==== Часть `:__group__` _должна_ использоваться _только для одного_ файла дистрибутива. Она служит уникальным ключом, и её повторное использование приведёт к перезаписи предыдущих значений. ==== [NOTE] ==== Поскольку это всего лишь синтаксический сахар над `DISTFILES` и `MASTER_SITES`, имена групп должны соответствовать ограничениям на имена групп, описанным в crossref:makefiles[porting-master-sites-n, Файлы дистрибутивов или патчей из нескольких источников] ==== При получении нескольких файлов с использованием GitLab иногда файл дистрибутива по умолчанию не загружается с сайта GitLab. Чтобы отключить загрузку файла дистрибутива по умолчанию, установите: [.programlisting] .... USE_GITLAB= nodefault .... [IMPORTANT] ==== При использовании `USE_GITLAB=nodefault`, [.filename]#Makefile# должен устанавливать `DISTFILES` в своём crossref:makefiles[porting-order-portname,верхнем блоке]. Определение должно быть следующим: [.programlisting] .... DISTFILES= ${DISTNAME}${EXTRACT_SUFX} .... ==== [[makefile-master_sites-gitlab-multi]] .Использование `USE_GITLAB` с несколькими файлами дистрибутива [example] ==== Время от времени возникает необходимость загрузить более одного файла дистрибутива. Например, когда вышестоящий git-репозиторий использует подмодули. Это можно легко сделать с помощью групп в переменных `GL_*`: [.programlisting] .... PORTNAME= foo DISTVERSION= 1.0.2 USE_GITLAB= yes GL_SITE= https://gitlab.example.com:9434/gitlab:icons GL_ACCOUNT= bar:icons,contrib GL_PROJECT= foo-icons:icons foo-contrib:contrib GL_COMMIT= c189207a55da45305c884fe2b50e086fcad4724b ae7368cab1ca7ca754b38d49da064df87968ffe4:icons 9e4dd76ad9b38f33fdb417a4c01935958d5acd2a:contrib GL_SUBDIR= ext/icons:icons CONFIGURE_ARGS= --with-contrib=${WRKSRC_contrib} .... Это загрузит два файла дистрибутива с gitlab.com и один с `gitlab.example.com`, где размещается GitLab. По умолчанию файл берется из [.filename]#https://gitlab.com/foo/foo#, а коммит — `c189207a55da45305c884fe2b50e086fcad4724b`. Второй файл, из группы `icons`, берется из [.filename]#https://gitlab.example.com:9434/gitlab/bar/foo-icons#, а коммит — `ae7368cab1ca7ca754b38d49da064df87968ffe4`. Третий файл берется из [.filename]#https://gitlab.com/bar/foo-contrib#, а коммит — `9e4dd76ad9b38f33fdb417a4c01935958d5acd2a`. Файлы дистрибутива называются [.filename]#foo-foo-c189207a55da45305c884fe2b50e086fcad4724b_GL0.tar.gz#, [.filename]#bar-foo-icons-ae7368cab1ca7ca754b38d49da064df87968ffe4_GL0.tar.gz# и [.filename]#bar-foo-contrib-9e4dd76ad9b38f33fdb417a4c01935958d5acd2a_GL0.tar.gz#. Все файлы дистрибутива извлекаются в `${WRKDIR}` в соответствующих подкаталогах. Основной файл по-прежнему извлекается в `${WRKSRC}`, в данном случае это [.filename]#${WRKDIR}/foo-c189207a55da45305c884fe2b50e086fcad4724b-c189207a55da45305c884fe2b50e086fcad4724b#. Каждый дополнительный файл дистрибутива извлекается в `${WRKSRC_group}`. Здесь для группы `icons` он называется `${WRKSRC_icons}` и содержит [.filename]#${WRKDIR}/foo-icons-ae7368cab1ca7ca754b38d49da064df87968ffe4-ae7368cab1ca7ca754b38d49da064df87968ffe4#. Файл группы `contrib` называется `${WRKSRC_contrib}` и содержит `${WRKDIR}/foo-contrib-9e4dd76ad9b38f33fdb417a4c01935958d5acd2a-9e4dd76ad9b38f33fdb417a4c01935958d5acd2a`. Система сборки программного обеспечения ожидает найти иконки в подкаталоге [.filename]#ext/icons# в своих исходниках, поэтому используется `GL_SUBDIR`. `GL_SUBDIR` гарантирует, что [.filename]#ext# существует, но [.filename]#ext/icons# ещё не существует. Затем она выполняет следующее: [.programlisting] .... post-extract: @${MV} ${WRKSRC_icons} ${WRKSRC}/ext/icons .... ==== [[makefile-master_sites-gitlab-multi2]] .Использование `USE_GITLAB` с несколькими файлами дистрибуции с помощью `GL_TUPLE` [example] ==== Это функционально эквивалентно crossref:makefiles[makefile-master_sites-gitlab-multi,Использование `USE_GITLAB` с несколькими файлами дистрибуции], но с использованием `GL_TUPLE`: [.programlisting] .... PORTNAME= foo DISTVERSION= 1.0.2 USE_GITLAB= yes GL_COMMIT= c189207a55da45305c884fe2b50e086fcad4724b GL_TUPLE= https://gitlab.example.com:9434/gitlab:bar:foo-icons:ae7368cab1ca7ca754b38d49da064df87968ffe4:icons/ext/icons \ bar:foo-contrib:9e4dd76ad9b38f33fdb417a4c01935958d5acd2a:contrib CONFIGURE_ARGS= --with-contrib=${WRKSRC_contrib} .... В предыдущем примере использовалась группировка с `bar:icons,contrib`. Некоторую избыточную информацию приходится указывать с `GL_TUPLE`, так как группировка невозможна. ==== [[makefile-extract_sufx]] === `EXTRACT_SUFX` Если имеется один файл дистрибутива, и он использует нестандартное суффикс для указания механизма сжатия, установите `EXTRACT_SUFX`. Например, если файл дистрибутива был назван [.filename]#foo.tar.gzip# вместо более привычного [.filename]#foo.tar.gz#, напишите: [.programlisting] .... DISTNAME= foo EXTRACT_SUFX= .tar.gzip .... `USES=tar[:__xxx__]`, `USES=lha` или `USES=zip` автоматически устанавливают `EXTRACT_SUFX` в наиболее распространённые расширения архивов при необходимости, подробнее см. crossref:uses[uses,Использование макросов `USES`]. Если ни один из них не задан, `EXTRACT_SUFX` по умолчанию принимает значение `.tar.gz`. [NOTE] ==== Как `EXTRACT_SUFX` используется только в `DISTFILES`, следует задавать только один из них. ==== [[makefile-distfiles-definition]] === `DISTFILES` Иногда названия файлов для загрузки не имеют ничего общего с именем порта. Например, файл может называться [.filename]#source.tar.gz# или подобным образом. В других случаях исходный код приложения может быть разбит на несколько различных архивов, все из которых необходимо загрузить. Если это так, установите `DISTFILES` как список разделённых пробелами файлов, которые необходимо загрузить. [.programlisting] .... DISTFILES= source1.tar.gz source2.tar.gz .... Если явно не задано, `DISTFILES` по умолчанию равно `${DISTNAME}${EXTRACT_SUFX}`. [[makefile-extract_only]] === `EXTRACT_ONLY` Если необходимо извлечь только некоторые из `DISTFILES` — например, один из них является исходным кодом, а другой — несжатым документом — укажите имена файлов, которые нужно извлечь, в `EXTRACT_ONLY`. [.programlisting] .... DISTFILES= source.tar.gz manual.html EXTRACT_ONLY= source.tar.gz .... Если ни один из `DISTFILES` не требует распаковки, установите `EXTRACT_ONLY` в пустую строку. [.programlisting] .... EXTRACT_ONLY= .... [[porting-patchfiles]] === `PATCHFILES` Если порт требует дополнительных исправлений, доступных через FTP или HTTP, установите `PATCHFILES` в имена файлов, а `PATCH_SITES` — в URL каталога, содержащего их (формат такой же, как у `MASTER_SITES`). Если патч не относится к корню исходного дерева (то есть к `WRKSRC`), потому что содержит дополнительные пути, установите `PATCH_DIST_STRIP` соответствующим образом. Например, если все пути в патче имеют дополнительный префикс `foozolix-1.0/` перед именами файлов, задайте `PATCH_DIST_STRIP=-p1`. Не беспокойтесь, если патчи сжаты; они будут автоматически распакованы, если их имена заканчиваются на [.filename]#.Z#, [.filename]#.gz#, [.filename]#.bz2# или [.filename]#.xz#. Если патч распространяется вместе с другими файлами, такими как документация, в сжатом tarball, использование `PATCHFILES` невозможно. В таком случае добавьте имя и расположение tarball с патчами в `DISTFILES` и `MASTER_SITES`. Затем используйте `EXTRA_PATCHES`, чтобы указать на эти файлы, и [.filename]#bsd.port.mk# автоматически применит их. В частности, _не_ копируйте файлы патчей в [.filename]#${PATCHDIR}#. Этот каталог может быть недоступен для записи. [TIP] ==== Если есть несколько патчей и для них требуются разные значения параметра strip, его можно добавить рядом с именем патча в `PATCHFILES`, например: [.programlisting] .... PATCHFILES= patch1 patch2:-p1 .... Это не конфликтует с crossref:makefiles[porting-master-sites-n,функцией группировки мастер-сайтов], добавление группы также работает: [.programlisting] .... PATCHFILES= patch2:-p1:source2 .... ==== [NOTE] ==== Tarball уже будет распакован вместе с обычными исходными кодами, поэтому нет необходимости явно его распаковывать, если это обычный сжатый tarball. Будьте особенно осторожны, чтобы не перезаписать существующие файлы в этом каталоге при ручной распаковке. Также не забудьте добавить команду для удаления скопированного патча в цель `pre-clean`. ==== [[porting-master-sites-n]] === Несколько файлов дистрибутивов или исправлений из нескольких местоположений (Считайте, что это несколько «продвинутая тема»; тем, кто впервые читает этот документ, возможно, стоит сначала пропустить этот раздел). Этот раздел содержит информацию о механизме загрузки, известном как `MASTER_SITES:n` и `MASTER_SITES_NN`. Мы будем называть этот механизм `MASTER_SITES:n`. Небольшая предыстория. В OpenBSD есть удобная функция внутри `DISTFILES` и `PATCHFILES`, которая позволяет добавлять постфикс `:n` к файлам и патчам. Здесь `n` может быть любым словом, содержащим `[0-9a-zA-Z_]`, и обозначать группу. Например: [.programlisting] .... DISTFILES= alpha:0 beta:1 .... В OpenBSD файл дистрибутива [.filename]#alpha# будет связан с переменной `MASTER_SITES0`, а не с нашей общей `MASTER_SITES`, а [.filename]#beta# — с `MASTER_SITES1`. Это очень интересная функция, которая может сократить бесконечные поиски нужного сайта для загрузки. Представьте 2 файла в `DISTFILES` и 20 сайтов в `MASTER_SITES`, причём сайты медленные как черепаха, где [.filename]#beta# есть на всех сайтах из `MASTER_SITES`, а [.filename]#alpha# можно найти только на 20-м сайте. Было бы так обидно проверять их все, если бы сопровождающий знал это заранее, не так ли? Не самое лучшее начало для чудесных выходных! Теперь, когда идея понятна, представьте больше `DISTFILES` и больше `MASTER_SITES`. Безусловно, наш "мастер по исследованию distfiles" оценил бы снижение нагрузки на сеть, которое это принесло бы. В следующих разделах будет приведена информация о реализации этой идеи в FreeBSD. Мы немного улучшили концепцию OpenBSD. [IMPORTANT] ==== Имена групп не могут содержать дефисы (`-`), более того, они не могут содержать любые символы вне диапазона `[a-zA-Z0-9_]`. Это связано с тем, что, хотя man:make[1] допускает использование имён переменных с дефисами, man:sh[1] — нет. ==== [[porting-master-sites-n-simplified]] ==== Упрощенная информация В этом разделе объясняется, как быстро настроить детализированное получение нескольких файлов дистрибутивов и патчей с разных сайтов и подкаталогов. Здесь описывается случай упрощённого использования `MASTER_SITES:n`. Этого будет достаточно для большинства сценариев. Более подробная информация доступна в crossref:makefiles[ports-master-sites-n-detailed, Подробная Информация]. Некоторые приложения состоят из нескольких распространяемых файлов, которые необходимо загрузить с различных сайтов. Например, Ghostscript включает основную часть программы и множество драйверов, используемых в зависимости от принтера пользователя. Некоторые из этих драйверов поставляются вместе с основной частью, но многие другие необходимо загружать с различных сайтов. Для поддержки этого, каждая запись в `DISTFILES` может сопровождаться двоеточием и "именем группы". Затем каждый сайт, указанный в `MASTER_SITES`, сопровождается двоеточием и группой, которая указывает, какие файлы дистрибутива загружаются с данного сайта. Например, рассмотрим приложение, исходный код которого разделён на две части: [.filename]#source1.tar.gz# и [.filename]#source2.tar.gz#, которые необходимо загрузить с двух разных сайтов. В [.filename]#Makefile# порта будут присутствовать строки, подобные crossref:makefiles[ports-master-sites-n-example-simple-use-one-file-per-site,Упрощённое использование `MASTER_SITES:n` с одним файлом на сайт]. [[ports-master-sites-n-example-simple-use-one-file-per-site]] .Упрощённое использование `MASTER_SITES:n` с одним файлом на сайт [example] ==== [.programlisting] .... MASTER_SITES= ftp://ftp1.example.com/:source1 \ http://www.example.com/:source2 DISTFILES= source1.tar.gz:source1 \ source2.tar.gz:source2 .... ==== Несколько файлов дистрибутивов могут принадлежать одной группе. Продолжая предыдущий пример, предположим, что существует третий файл дистрибутива [.filename]#source3.tar.gz#, который загружается с `ftp.example2.com`. Тогда [.filename]#Makefile# будет записан, как показано в crossref:makefiles[ports-master-sites-n-example-simple-use-more-than-one-file-per-site,Упрощённое использование `MASTER_SITES:n` с несколькими файлами на один сайт]. [[ports-master-sites-n-example-simple-use-more-than-one-file-per-site]] .Упрощённое использование `MASTER_SITES:n` с несколькими файлами на одном сайте [example] ==== [.programlisting] .... MASTER_SITES= ftp://ftp.example.com/:source1 \ http://www.example.com/:source2 DISTFILES= source1.tar.gz:source1 \ source2.tar.gz:source2 \ source3.tar.gz:source2 .... ==== [[ports-master-sites-n-detailed]] ==== Подробная информация Хорошо, значит, предыдущий пример не отражал потребности нового порта? В этом разделе мы подробно объясним, как работает механизм детализированного получения `MASTER_SITES:n` и как его можно использовать. . Элементы могут иметь постфикс `:__n__`, где _n_ — это `[^:,]+`, то есть _n_ концептуально может быть любой буквенно-цифровой строкой, но пока мы ограничим её `[a-zA-Z_][0-9a-zA-Z_]+`. + Более того, сравнение строк чувствительно к регистру; то есть, `n` отличается от `N`. + Однако эти слова не могут использоваться для постфиксных целей, так как имеют специальное значение: `default`, `all` и `ALL` (они используются внутри системы, см. crossref:makefiles[porting-master-sites-n-what-changes-in-port-targets, ii]). Кроме того, `DEFAULT` является словом специального назначения (проверьте пункт crossref:makefiles[porting-master-sites-n-DEFAULT-group,3]). . Элементы с постфиксом `:n` принадлежат группе `n`, `:m` — группе `m` и так далее. + . [[porting-master-sites-n-DEFAULT-group]] Элементы без постфикса не принадлежат к группам, все они относятся к специальной группе `DEFAULT`. Элементы с постфиксом `DEFAULT` избыточны, за исключением случаев, когда элемент одновременно принадлежит и к `DEFAULT`, и к другим группам (см. пункт crossref:makefiles[porting-master-sites-n-comma-operator,5]). + Эти примеры эквивалентны, но первый предпочтительнее: + [.programlisting] .... MASTER_SITES= alpha .... + [.programlisting] .... MASTER_SITES= alpha:DEFAULT .... . Группы не являются исключительными, элемент может принадлежать нескольким разным группам одновременно, а группа может содержать несколько разных элементов или не содержать их вовсе. + . [[porting-master-sites-n-comma-operator]] Когда элемент принадлежит нескольким группам одновременно, используйте оператор запятую (`,`). + Вместо повторения несколько раз, каждый раз с разным постфиксом, мы можем перечислить несколько групп сразу в одном постфиксе. Например, `:m,n,o` обозначает элемент, принадлежащий группам `m`, `n` и `o`. + Все эти примеры эквивалентны, но последний является предпочтительным: + [.programlisting] .... MASTER_SITES= alpha alpha:SOME_SITE .... + [.programlisting] .... MASTER_SITES= alpha:DEFAULT alpha:SOME_SITE .... + [.programlisting] .... MASTER_SITES= alpha:SOME_SITE,DEFAULT .... + [.programlisting] .... MASTER_SITES= alpha:DEFAULT,SOME_SITE .... . Все сайты в заданной группе сортируются согласно `MASTER_SORT_AWK`. Все группы в `MASTER_SITES` и `PATCH_SITES` также сортируются. + . [[porting-master-sites-n-group-semantics]] Семантика групп может использоваться в любых переменных `MASTER_SITES`, `PATCH_SITES`, `MASTER_SITE_SUBDIR`, `PATCH_SITE_SUBDIR`, `DISTFILES` и `PATCHFILES` согласно следующему синтаксису: .. Все элементы `MASTER_SITES`, `PATCH_SITES`, `MASTER_SITE_SUBDIR` и `PATCH_SITE_SUBDIR` должны заканчиваться символом дробной черты `/`. Если элементы принадлежат к какой-либо группе, постфикс группы `:__n__` должен следовать сразу после завершающего символа `/`. Механизм `MASTER_SITES:n` полагается на наличие завершающего символа `/`, чтобы избежать путаницы между элементами, где `:n` является допустимой частью элемента, и случаями, где `:n` обозначает группу `n`. В целях совместимости, поскольку ранее завершающий символ `/` не требовался в элементах `MASTER_SITE_SUBDIR` и `PATCH_SITE_SUBDIR`, если символ, непосредственно предшествующий постфиксу, не является `/`, то `:n` будет считаться допустимой частью элемента, а не постфиксом группы, даже если элемент оканчивается на `:n`. См. оба раздела crossref:makefiles[ports-master-sites-n-example-detailed-use-master-site-subdir,Подробное использование `MASTER_SITES:n` в `MASTER_SITE_SUBDIR`] и crossref:makefiles[ports-master-sites-n-example-detailed-use-complete-example-master-sites,Подробное использование `MASTER_SITES:n` с оператором запятая, несколькими файлами, несколькими сайтами и несколькими подкаталогами]. + [[ports-master-sites-n-example-detailed-use-master-site-subdir]] .Подробное использование `MASTER_SITES:n` в `MASTER_SITE_SUBDIR` [example] ==== [.programlisting] .... MASTER_SITE_SUBDIR= old:n new/:NEW .... *** Каталоги в группе `DEFAULT` -> old:n *** Каталоги в группе `NEW` -> new ==== + [[ports-master-sites-n-example-detailed-use-complete-example-master-sites]] .Подробное использование `MASTER_SITES:n` с оператором запятая, несколькими файлами, сайтами и подкаталогами [example] ==== [.programlisting] .... MASTER_SITES= http://site1/%SUBDIR%/ http://site2/:DEFAULT \ http://site3/:group3 http://site4/:group4 \ http://site5/:group5 http://site6/:group6 \ http://site7/:DEFAULT,group6 \ http://site8/%SUBDIR%/:group6,group7 \ http://site9/:group8 DISTFILES= file1 file2:DEFAULT file3:group3 \ file4:group4,group5,group6 file5:grouping \ file6:group7 MASTER_SITE_SUBDIR= directory-trial:1 directory-n/:groupn \ directory-one/:group6,DEFAULT \ directory .... Предыдущий пример приводит к такой детализированной загрузке файлов. Сайты перечислены в точном порядке их использования. *** [.filename]#file1# будет загружен из **** `MASTER_SITE_OVERRIDE` **** http://site1/directory-trial:1/ **** http://site1/directory-one/ **** http://site1/directory/ **** http://site2/ **** http://site7/ **** `MASTER_SITE_BACKUP` *** [.filename]#file2# будет загружен точно так же, как [.filename]#file1#, поскольку они оба принадлежат к одной и той же группе **** `MASTER_SITE_OVERRIDE` **** http://site1/directory-trial:1/ **** http://site1/directory-one/ **** http://site1/directory/ **** http://site2/ **** http://site7/ **** `MASTER_SITE_BACKUP` *** [.filename]#file3# будет загружен из **** `MASTER_SITE_OVERRIDE` **** http://site3/ **** `MASTER_SITE_BACKUP` *** [.filename]#file4# будет загружен из **** `MASTER_SITE_OVERRIDE` **** http://site4/ **** http://site5/ **** http://site6/ **** http://site7/ **** http://site8/directory-one/ **** `MASTER_SITE_BACKUP` *** [.filename]#file5# будет загружен из **** `MASTER_SITE_OVERRIDE` **** `MASTER_SITE_BACKUP` *** [.filename]#file6# будет получен из **** `MASTER_SITE_OVERRIDE` **** http://site8/ **** `MASTER_SITE_BACKUP` ==== . Как сгруппировать один из специальных макросов из [.filename]#bsd.sites.mk#, например, SourceForge (`SF`)? + Это максимально упрощено. См. crossref:makefiles[ports-master-sites-n-example-detailed-use-master-site-sourceforge,Подробное использование `MASTER_SITES:n` с SourceForge (`SF`)]. + [[ports-master-sites-n-example-detailed-use-master-site-sourceforge]] .Подробное использование `MASTER_SITES:n` с SourceForge (`SF`) [example] ==== [.programlisting] .... MASTER_SITES= http://site1/ SF/something/1.0:sourceforge,TEST DISTFILES= something.tar.gz:sourceforge .... [.filename]#something.tar.gz# будет загружен со всех сайтов в пределах SourceForge. ==== . Как использовать это с `PATCH*`? + Все примеры были выполнены с `MASTER*`, но они работают точно так же для `PATCH*`, как можно увидеть в crossref:makefiles[ports-master-sites-n-example-detailed-use-patch-sites,Упрощённое использование `MASTER_SITES:n` с `PATCH_SITES`]. + [[ports-master-sites-n-example-detailed-use-patch-sites]] .Упрощённое использование `MASTER_SITES:n` с `PATCH_SITES` [example] ==== [.programlisting] .... PATCH_SITES= http://site1/ http://site2/:test PATCHFILES= patch1:test .... ==== [[port-master-sites-n-what-changed]] ==== Что меняется для портов? Что остаётся неизменным? [lowerroman] . Все текущие порты остаются без изменений. Функция `MASTER_SITES:n` активируется только при наличии элементов с постфиксом `:__n__`, соответствующих указанным выше синтаксическим правилам, в частности, как показано в пункте crossref:makefiles[porting-master-sites-n-group-semantics, 7]. + . [[porting-master-sites-n-what-changes-in-port-targets]] Порты сохраняют те же цели: `checksum`, `makesum`, `patch`, `configure`, `build` и т.д., за исключением очевидных случаев: `do-fetch`, `fetch-list`, `master-sites` и `patch-sites`. ** `do-fetch`: развертывает новую группировку с постфиксом `DISTFILES` и `PATCHFILES` с соответствующими групповыми элементами в `MASTER_SITES` и `PATCH_SITES`, которые используют соответствующие групповые элементы в `MASTER_SITE_SUBDIR` и `PATCH_SITE_SUBDIR`. Проверьте crossref:makefiles[ports-master-sites-n-example-detailed-use-complete-example-master-sites,Подробное использование `MASTER_SITES:n` с оператором запятой, множественными файлами, множественными сайтами и множественными подкаталогами]. ** `fetch-list`: работает как старый `fetch-list`, за исключением того, что группировка происходит так же, как в `do-fetch`. ** `master-sites` и `patch-sites`: (несовместимо с более старыми версиями) возвращают только элементы группы `DEFAULT`; фактически они выполняют цели `master-sites-default` и `patch-sites-default` соответственно. + Кроме того, предпочтительнее использовать цель `master-sites-all` или `patch-sites-all`, чем напрямую проверять `MASTER_SITES` или `PATCH_SITES`. Кроме того, прямая проверка не гарантирует работу в будущих версиях. Для получения дополнительной информации об этих новых целях портов см. пункт crossref:makefiles[porting-master-sites-n-new-port-targets-master-sites-all, B]. . Новые цели портов .. Существуют цели `master-sites-_n_` и `patch-sites-_n_`, которые будут выводить элементы соответствующей группы _n_ в `MASTER_SITES` и `PATCH_SITES` соответственно. Например, и `master-sites-DEFAULT`, и `patch-sites-DEFAULT` вернут элементы группы `DEFAULT`, `master-sites-test` и `patch-sites-test` — группы `test`, и так далее. + .. [[porting-master-sites-n-new-port-targets-master-sites-all]] Существуют новые цели `master-sites-all` и `patch-sites-all`, которые выполняют работу старых `master-sites` и `patch-sites`. Они возвращают элементы всех групп, как если бы они все принадлежали одной группе, с оговоркой, что перечисляется столько же `MASTER_SITE_BACKUP` и `MASTER_SITE_OVERRIDE`, сколько определено групп в `DISTFILES` или `PATCHFILES`; соответственно для `master-sites-all` и `patch-sites-all`. [[makefile-dist_subdir]] === `DIST_SUBDIR` Не допускайте захламления портом каталога [.filename]#/usr/ports/distfiles#. Если порт требует загрузки большого количества файлов или содержит файл с именем, которое может конфликтовать с другими портами (например, [.filename]#Makefile#), установите `DIST_SUBDIR` в имя порта (подойдут `${PORTNAME}` или `${PKGNAMEPREFIX}${PORTNAME}`). Это изменит `DISTDIR` со значения по умолчанию [.filename]#/usr/ports/distfiles# на [.filename]#/usr/ports/distfiles/${DIST_SUBDIR}#, фактически помещая все необходимые для порта файлы в этот подкаталог. Также будет проверяться подкаталог с тем же именем на основном резервном сайте по адресу http://distcache.FreeBSD.org[http://distcache.FreeBSD.org] (Явное указание `DISTDIR` в [.filename]#Makefile# не решит эту задачу, поэтому используйте `DIST_SUBDIR`.) [NOTE] ==== Это не влияет на сайты в `MASTER_SITES`, определённые в [.filename]#Makefile#. ==== [[makefile-maintainer]] == `MAINTAINER` Установите здесь свой адрес электронной почты. Пожалуйста. _:-)_ Только один адрес без комментария допускается в качестве значения `MAINTAINER`. Используемый формат: `user@hostname.domain`. Пожалуйста, не включайте в эту запись описательный текст, например, настоящее имя. Это только вносит путаницу в инфраструктуру Ports и большинство инструментов, которые её используют. Ответственный за поддержку порта обязан поддерживать порт в актуальном состоянии и обеспечивать его корректную работу. Подробное описание обязанностей ответственного за поддержку порта приведено в разделе extref:{contributing}[Задача для сопровождающих портов,maintain-port]. [NOTE] ==== Сопровождающий добровольно поддерживает порт в рабочем состоянии. Сопровождающие несут основную ответственность за свои порты, но не имеют исключительных прав на них. Порты существуют для пользы сообщества и, по сути, принадлежат сообществу. Это означает, что люди, не являющиеся сопровождающими, также могут вносить изменения в порт. Крупные изменения в коллекции портов могут потребовать правок во многих портах. Команда управления портами FreeBSD или члены других команд могут изменять порты для исправления проблем с зависимостями или других проблем, таких как обновление версии динамической библиотеки. Некоторые типы исправлений имеют "автоматическое согласование" от {portmgr}, что позволяет любому коммиттеру исправлять эти категории проблем в любом порте. Такие исправления не требуют одобрения от сопровождающего. Автоматическое согласование для большинства портов применяется к исправлениям, таким как изменения инфраструктуры, или тривиальным и _проверенным_ исправлениям сборки и выполнения. Текущий список доступен в extref:{committers-guide}[разделе Портов Руководства коммиттера, ports-qa-misc-blanket-approval]. ==== Другие изменения в порте будут отправлены сопровождающему на проверку и утверждение перед внесением. Если сопровождающий не отвечает на запрос об обновлении в течение двух недель (за исключением основных государственных праздников), это считается превышением времени ожидания сопровождающего, и обновление может быть внесено без его явного одобрения. Если сопровождающий не отвечает в течение трёх месяцев или если произошло три последовательных превышения времени ожидания, то сопровождающий считается отсутствующим без уведомления, и все его порты могут быть возвращены в общий пул. Исключениями являются порты, сопровождаемые {portmgr} или {security-officer}. Никакие несанкционированные изменения не могут быть внесены в порты, сопровождаемые этими группами. Мы оставляем за собой право изменять представленные сопровождающим материалы, чтобы лучше соответствовать существующим политикам и стилю Коллекции портов, без явного одобрения отправителя или сопровождающего. Кроме того, масштабные инфраструктурные изменения могут привести к модификации порта без согласия сопровождающего. Подобные изменения никогда не повлияют на функциональность порта. {portmgr} оставляет за собой право отозвать или изменить права сопровождающего по любой причине, а {security-officer} оставляет за собой право отозвать или изменить права сопровождающего по соображениям безопасности. [[makefile-comment]] == `COMMENT` Комментарий — это однострочное описание порта, отображаемое командой `pkg info`. При составлении придерживайтесь следующих правил: . Строка COMMENT должна быть не длиннее 70 символов. . Не включайте название пакета или номер версии программного обеспечения. . Комментарий должен начинаться с заглавной буквы и заканчиваться без точки. . Не начинайте с неопределённого артикля (то есть A или An). . Пишите названия с заглавной буквы, например: Apache, JavaScript или Perl. . Используйте запятую для списков слов: "green, red, and blue." . Проверяйте на наличие орфографических ошибок. Вот пример: [.programlisting] .... COMMENT= Cat chasing a mouse all over the screen .... Переменная COMMENT следует сразу за переменной MAINTAINER в файле [.filename]#Makefile#. [[makefile-www]] == Веб-сайт проекта Каждый порт должен указывать на веб-сайт, предоставляющий дополнительную информацию о программном обеспечении. Везде, где это возможно, следует использовать официальный сайт проекта, поддерживаемый разработчиками программного обеспечения. [.programlisting] .... WWW= https://ffmpeg.org/ .... Но это также может быть каталог или ресурс в репозитории исходного кода: [.programlisting] .... WWW= https://sourceforge.net/projects/mpd/ .... Переменная WWW следует сразу за переменной COMMENT в файле [.filename]#Makefile#. Если один и тот же контент доступен по HTTP и HTTPS, следует использовать URL, начинающийся с `https://`. Если URI является корнем веб-сайта или каталогом, он должен заканчиваться косой чертой. Эта информация ранее размещалась в последней строке файла [.filename]#pkg-descr#. Она была перенесена в Makefile для удобства обслуживания и обработки. Наличие строки `WWW:` в конце файла [.filename]#pkg-descr# считается устаревшим. [[licenses]] == Лицензии Каждый порт должен содержать документацию о лицензии, под которой он распространяется. Если лицензия не одобрена OSI, необходимо также указать любые ограничения на распространение. [[licenses-license]] === `LICENSE` Краткое название лицензии или лицензий, если применяется более одной лицензии. Если это одна из лицензий, перечисленных в crossref:makefiles[licenses-license-list,Предопределённый список лицензий], можно задать только переменные `LICENSE_FILE` и `LICENSE_DISTFILES`. Если это лицензия, которая не определена в рамках портов (см. crossref:makefiles[licenses-license-list,Список предопределённых лицензий]), необходимо задать `LICENSE_PERMS` и `LICENSE_NAME`, а также `LICENSE_FILE` или `LICENSE_TEXT`. Также можно задать `LICENSE_DISTFILES` и `LICENSE_GROUPS`, но это не обязательно. Предопределённые лицензии показаны в crossref:makefiles[licenses-license-list,Список предопределённых лицензий]. Текущий список всегда доступен в [.filename]#Mk/bsd.licenses.db.mk#. [[licenses-license-ex1]] .Простейшее использование, предопределённые лицензии [example] ==== Когда в файле [.filename]#README# какого-либо программного обеспечения указано: «Данное программное обеспечение распространяется на условиях GNU Lesser General Public License, опубликованной Free Software Foundation; либо версии 2.1 Лицензии, либо (по вашему выбору) любой более поздней версии», но сам файл лицензии не предоставлен, используйте следующее: [.programlisting] .... LICENSE= LGPL21+ .... Когда программное обеспечение предоставляет файл лицензии, используйте это: [.programlisting] .... LICENSE= LGPL21+ LICENSE_FILE= ${WRKSRC}/COPYING .... ==== Для предопределённых лицензий права по умолчанию: `dist-mirror dist-sell pkg-mirror pkg-sell auto-accept`. [[licenses-license-list]] .Предопределённый список лицензий [cols="1,1,1,1", frame="none", options="header"] |=== | Короткое имя | Имя | Группа | Разрешения |`AGPLv3` |Универсальная общественная лицензия GNU Affero версии 3 |`FSF GPL OSI` |(по умолчанию) |`AGPLv3+` |Универсальная общественная лицензия GNU Affero версии 3 (или позднее) |`FSF GPL OSI` |(по умолчанию) |`APACHE10` |Apache License 1.0 |`FSF` |(по умолчанию) |`APACHE11` |Apache License 1.1 |`FSF OSI` |(по умолчанию) |`APACHE20` |Apache License 2.0 |`FSF OSI` |(по умолчанию) |`ART10` |Художественная лицензия версия 1.0 |`OSI` |(по умолчанию) |`ART20` |Художественная лицензия версии 2.0 |`FSF GPL OSI` |(по умолчанию) |`ARTPERL10` |Художественная лицензия (perl) версия 1.0 |`OSI` |(по умолчанию) |`BSD` |Лицензия BSD, общая версия (устарела) |`FSF OSI COPYFREE` |(по умолчанию) |`BSD2CLAUSE` |BSD 2-пунктная лицензия "Упрощенная" |`FSF OSI COPYFREE` |(по умолчанию) |`BSD3CLAUSE` |BSD 3-пунктная лицензия "Новая" или "Пересмотренная" |`FSF OSI COPYFREE` |(по умолчанию) |`BSD4CLAUSE` |BSD 4-пунктная лицензия "Оригинальная" или "Старая" |`FSF` |(по умолчанию) |`BSL` |Лицензия программного обеспечения Boost |`FSF OSI COPYFREE` |(по умолчанию) |`CC-BY-1.0` |Creative Commons с указанием авторства 1.0 -| +| |(по умолчанию) |`CC-BY-2.0` |Creative Commons с указанием авторства 2.0 -| +| |(по умолчанию) |`CC-BY-2.5` |Creative Commons с указанием авторства 2.5 -| +| |(по умолчанию) |`CC-BY-3.0` |Creative Commons с указанием авторства 3.0 -| +| |(по умолчанию) |`CC-BY-4.0` |Creative Commons с указанием авторства 4.0 -| +| |(по умолчанию) |`CC-BY-NC-1.0` |Creative Commons с указанием авторства – некоммерческая 1.0 -| +| |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-2.0` |Creative Commons с указанием авторства – некоммерческая 2.0 -| +| |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-2.5` |Creative Commons с указанием авторства – некоммерческая 2.5 -| +| |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-3.0` |Creative Commons с указанием авторства – некоммерческая 3.0 -| +| |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-4.0` |Creative Commons с указанием авторства – некоммерческая 4.0 -| +| |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-ND-1.0` |Creative Commons с указанием авторства – некоммерческая – без производных 1.0 -| +| |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-ND-2.0` |Creative Commons с указанием авторства – некоммерческая – без производных 2.0 -| +| |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-ND-2.5` |Creative Commons с указанием авторства – некоммерческая – без производных 2.5 -| +| |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-ND-3.0` |Creative Commons с указанием авторства – некоммерческая – без производных 3.0 -| +| |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-ND-4.0` |Creative Commons с указанием авторства – некоммерческая – без производных 4.0 -| +| |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-SA-1.0` |Creative Commons с указанием авторства – некоммерческая – на тех же условиях 1.0 -| +| |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-SA-2.0` |Creative Commons с указанием авторства – некоммерческая – на тех же условиях 2.0 -| +| |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-SA-2.5` |Creative Commons с указанием авторства – некоммерческая – на тех же условиях 2.5 -| +| |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-SA-3.0` |Creative Commons с указанием авторства – некоммерческая – на тех же условиях 3.0 -| +| |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-SA-4.0` |Creative Commons с указанием авторства – некоммерческая – на тех же условиях 4.0 -| +| |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-ND-1.0` |Creative Commons с указанием авторства – без производных 1.0 -| +| |(по умолчанию) |`CC-BY-ND-2.0` |Creative Commons с указанием авторства – без производных 2.0 -| +| |(по умолчанию) |`CC-BY-ND-2.5` |Creative Commons с указанием авторства – без производных 2.5 -| +| |(по умолчанию) |`CC-BY-ND-3.0` |Creative Commons с указанием авторства – без производных 3.0 -| +| |(по умолчанию) |`CC-BY-ND-4.0` |Creative Commons с указанием авторства – без производных 4.0 -| +| |(по умолчанию) |`CC-BY-SA-1.0` |Creative Commons с указанием авторства – на тех же условиях 1.0 -| +| |(по умолчанию) |`CC-BY-SA-2.0` |Creative Commons с указанием авторства – на тех же условиях 2.0 -| +| |(по умолчанию) |`CC-BY-SA-2.5` |Creative Commons с указанием авторства – на тех же условиях 2.5 -| +| |(по умолчанию) |`CC-BY-SA-3.0` |Creative Commons с указанием авторства – на тех же условиях 3.0 -| +| |(по умолчанию) |`CC-BY-SA-4.0` |Creative Commons с указанием авторства – на тех же условиях 4.0 -| +| |(по умолчанию) |`CC0-1.0` |Creative Commons Zero v1.0 Universal (Отказ от прав 1.0 Универсальная) |`FSF GPL COPYFREE` |(по умолчанию) |`CDDL` |Лицензия на совместную разработку и распространение |`FSF OSI` |(по умолчанию) |`CPAL-1.0` |Публичная лицензия общего распространения с указанием авторства |`FSF OSI` |(по умолчанию) |`ClArtistic` |Уточнённая художественная лицензия |`FSF GPL OSI` |(по умолчанию) |`EPL` |Публичная лицензия Eclipse |`FSF OSI` |(по умолчанию) |`GFDL` |GNU Свободная лицензия на документацию |`FSF` |(по умолчанию) |`GMGPL` |Модифицированная Общедоступная лицензия GNAT |`FSF GPL OSI` |(по умолчанию) |`GPLv1` |Универсальная общественная лицензия GNU версии 1 |`FSF GPL OSI` |(по умолчанию) |`GPLv1+` |Универсальная общественная лицензия GNU версии 1 (или более поздняя) |`FSF GPL OSI` |(по умолчанию) |`GPLv2` |Универсальная общественная лицензия GNU версии 2 |`FSF GPL OSI` |(по умолчанию) |`GPLv2+` |Универсальная общественная лицензия GNU версии 2 (или более поздняя) |`FSF GPL OSI` |(по умолчанию) |`GPLv3` |Универсальная общественная лицензия GNU версии 3 |`FSF GPL OSI` |(по умолчанию) |`GPLv3+` |Универсальная общественная лицензия GNU версии 3 (или более поздняя) |`FSF GPL OSI` |(по умолчанию) |`GPLv3RLE` |Исключение для библиотеки времени выполнения GNU GPL версии 3 |`FSF GPL OSI` |(по умолчанию) |`GPLv3RLE+` |Исключение для библиотеки времени выполнения GNU GPL версии 3 (или более поздняя) |`FSF GPL OSI` |(по умолчанию) |`ISCL` |Лицензия Internet Systems Consortium |`FSF GPL OSI COPYFREE` |(по умолчанию) |`LGPL20` |Общедоступная лицензия GNU для библиотек, версия 2.0 |`FSF GPL OSI` |(по умолчанию) |`LGPL20+` |Общедоступная лицензия GNU для библиотек, версия 2.0 (или более поздняя) |`FSF GPL OSI` |(по умолчанию) |`LGPL21` |Универсальная общественная лицензия GNU ограниченного применения, версия 2.1 |`FSF GPL OSI` |(по умолчанию) |`LGPL21+` |Универсальная общественная лицензия GNU ограниченного применения, версия 2.1 (или более поздняя) |`FSF GPL OSI` |(по умолчанию) |`LGPL3` |Универсальная общественная лицензия GNU ограниченного применения, версия 3 |`FSF GPL OSI` |(по умолчанию) |`LGPL3+` |Универсальная общественная лицензия GNU ограниченного применения, версия 3 (или более поздней) |`FSF GPL OSI` |(по умолчанию) |`LPPL10` |Публичная лицензия проекта LaTeX, версия 1.0 |`FSF OSI` |`dist-mirror dist-sell` |`LPPL11` |Публичная лицензия проекта LaTeX, версия 1.1 |`FSF OSI` |`dist-mirror dist-sell` |`LPPL12` |Публичная лицензия проекта LaTeX, версия 1.2 |`FSF OSI` |`dist-mirror dist-sell` |`LPPL13` |Публичная лицензия проекта LaTeX, версия 1.3 |`FSF OSI` |`dist-mirror dist-sell` |`LPPL13a` |Публичная лицензия проекта LaTeX, версия 1.3a |`FSF OSI` |`dist-mirror dist-sell` |`LPPL13b` |Публичная лицензия проекта LaTeX, версия 1.3b |`FSF OSI` |`dist-mirror dist-sell` |`LPPL13c` |Публичная лицензия проекта LaTeX, версия 1.3c |`FSF OSI` |`dist-mirror dist-sell` |`MIT` |Лицензия MIT / Лицензия X11 |`COPYFREE FSF GPL OSI` |(по умолчанию) |`MPL10` |Публичная лицензия Mozilla, версия 1.0 |`FSF OSI` |(по умолчанию) |`MPL11` |Публичная лицензия Mozilla, версия 1.1 |`FSF OSI` |(по умолчанию) |`MPL20` |Публичная лицензия Mozilla, версия 2.0 |`FSF OSI` |(по умолчанию) |`NCSA` |Открытая лицензия Университета Иллинойса/NCSA |`COPYFREE FSF GPL OSI` |(по умолчанию) |`NONE` |Лицензия не указана -| +| |`none` |`OFL10` |Лицензия SIL Open Font версия 1.0 (https://scripts.sil.org/OFL/) |`FONTS` |(по умолчанию) |`OFL11` |Лицензия SIL Open Font версия 1.1 (https://scripts.sil.org/OFL/) |`FONTS` |(по умолчанию) |`OWL` |Лицензия Открытых Произведений (owl.apotheon.org) |`COPYFREE` |(по умолчанию) |`OpenSSL` |Лицензия OpenSSL |`FSF` |(по умолчанию) |`PD` |Общественное достояние |`GPL COPYFREE` |(по умолчанию) |`PHP202` |Лицензия PHP версии 2.02 |`FSF OSI` |(по умолчанию) |`PHP30` |Лицензия PHP версии 3.0 |`FSF OSI` |(по умолчанию) |`PHP301` |Лицензия PHP версии 3.01 |`FSF OSI` |(по умолчанию) |`PSFL` |Лицензия Python Software Foundation |`FSF GPL OSI` |(по умолчанию) |`PostgreSQL` |Лицензия PostgreSQL |`FSF GPL OSI COPYFREE` |(по умолчанию) |`RUBY` |Лицензия Ruby |`FSF` |(по умолчанию) |`UNLICENSE` |Отказ от лицензии (The Unlicense) |`COPYFREE FSF GPL` |(по умолчанию) |`WTFPL` |Публичная лицензия "Делай что хочешь" версия 2 |`GPL FSF COPYFREE` |(по умолчанию) |`WTFPL1` |Публичная лицензия "Делай что хочешь" версия 1 |`GPL FSF COPYFREE` |(по умолчанию) |`ZLIB` |Лицензия zlib |`GPL FSF OSI` |(по умолчанию) |`ZPL21` |Публичная лицензия Zope версия 2.1 |`GPL OSI` |(по умолчанию) |=== [[licenses-license_perms]] === `LICENSE_PERMS` и `LICENSE_PERMS_NAME_` Разрешения. Используйте `none`, если пусто. .Список разрешений лицензии [[licenses-license_perms-dist-mirror]] `dist-mirror`:: Разрешается распространение дистрибутивных файлов. Дистрибутивные файлы будут добавлены в CDN `MASTER_SITE_BACKUP` FreeBSD. [[licenses-license_perms-no-dist-mirror]] `no-dist-mirror`:: Распространение дистрибутивных файлов запрещено. Это эквивалентно установке crossref:special[porting-restrictions-restricted,`RESTRICTED`]. Дистрибутивные файлы _не_ будут добавлены в CDN `MASTER_SITE_BACKUP` FreeBSD. [[licenses-license_perms-dist-sell]] `dist-sell`:: Продажа файлов дистрибутива разрешена. Файлы дистрибутива будут присутствовать на образах установщика. [[licenses-license_perms-no-dist-sell]] `no-dist-sell`:: Продажа файлов дистрибутива запрещена. Это эквивалентно установке crossref:special[porting-restrictions-no_cdrom,`NO_CDROM`]. [[licenses-license_perms-pkg-mirror]] `pkg-mirror`:: Свободное распространение пакета разрешено. Пакет будет распространяться через CDN пакетов FreeBSD https://pkg.freebsd.org/[https://pkg.freebsd.org/]. [[licenses-license_perms-no-pkg-mirror]] `no-pkg-mirror`:: Свободное распространение пакета запрещено. Эквивалентно установке crossref:special[porting-restrictions-no_package,`NO_PACKAGE`]. Пакет _не_ будет распространяться через FreeBSD CDN для пакетов https://pkg.freebsd.org/[https://pkg.freebsd.org/]. [[licenses-license_perms-pkg-sell]] `pkg-sell`:: Продажа пакета разрешена. Пакет будет присутствовать на образах установщика. [[licenses-license_perms-no-pkg-sell]] `no-pkg-sell`:: Продажа пакета запрещена. Это эквивалентно установке crossref:special[porting-restrictions-no_cdrom,`NO_CDROM`]. Пакет _не_ будет присутствовать на образах установщика. [[licenses-license_perms-auto-accept]] `auto-accept`:: Лицензия принимается по умолчанию. Запросы на принятие лицензии не отображаются, если пользователь не определил `LICENSES_ASK`. Используйте это, если в лицензии не указано, что пользователь должен принять условия лицензии. [[licenses-license_perms-no-auto-accept]] `no-auto-accept`:: Лицензия не принимается по умолчанию. Пользователь всегда будет запрошен на подтверждение принятия данной лицензии. Это должно использоваться, если лицензия требует, чтобы пользователь принял её условия. Когда присутствуют и `_permission_`, и `no-_permission_`, то `no-_permission_` отменяет `_permission_`. Когда `_permission_` отсутствует, это считается как `no-_permission_`. [WARNING] ==== Некоторые отсутствующие разрешения могут сделать порт (и все зависящие от него порты) непригодными для использования пользователями пакетов: Порт без разрешения `auto-accept` никогда не будет собран, и все зависящие от него порты будут проигнорированы. Порт без разрешения `pkg-mirror`, а также любые порты, зависящие от него, будут удалены после сборки, что гарантирует их отсутствие в дистрибуции. ==== [[licenses-license_perms-ex1]] .Нестандартная лицензия [example] ==== Прочитайте условия лицензии и переведите их, используя доступные разрешения. [.programlisting] .... LICENSE= UNKNOWN LICENSE_NAME= unknown LICENSE_TEXT= This program is NOT in public domain.\ It can be freely distributed for non-commercial purposes only. LICENSE_PERMS= dist-mirror no-dist-sell pkg-mirror no-pkg-sell auto-accept .... ==== [[licenses-license_perms-ex2]] .Стандартные и нестандартные лицензии [example] ==== Прочитайте условия лицензии и укажите их, используя доступные разрешения. В случае сомнений обратитесь за разъяснениями на {freebsd-ports}. [.programlisting] .... LICENSE= WARSOW GPLv2 LICENSE_COMB= multi LICENSE_NAME_WARSOW= Warsow Content License LICENSE_FILE_WARSOW= ${WRKSRC}/docs/license.txt LICENSE_PERMS_WARSOW= dist-mirror pkg-mirror auto-accept .... Когда разрешения лицензий GPLv2 и UNKNOWN смешиваются, порт получает `dist-mirror dist-sell pkg-mirror pkg-sell auto-accept dist-mirror no-dist-sell pkg-mirror no-pkg-sell auto-accept`. Опции `no-_разрешения_` отменяют соответствующие _разрешения_. Итоговый список разрешений: _dist-mirror pkg-mirror auto-accept_. Файлы дистрибутива и пакеты не будут доступны в образах установщика. ==== [[licenses-license_groups]] === `LICENSE_GROUPS` и `LICENSE_GROUPS_NAME` Группы, к которым принадлежит лицензия. .Список предопределённых групп лицензий [[licenses-license_groups-FSF]] `FSF`:: Одобрено Free Software Foundation, см. https://www.fsf.org/licensing/[Команда по лицензированию и соответствию FSF]. [[licenses-license_groups-GPL]] `GPL`:: Совместимые с GPL [[licenses-license_groups-OSI]] `OSI`:: Одобрено OSI, см. страницу https://opensource.org/licenses/[Открытых лицензий]. [[licenses-license_groups-COPYFREE]] `COPYFREE`:: Соответствует определению стандарта Copyfree, см. страницу https://copyfree.org/standard/licenses/[лицензий Copyfree]. [[licenses-license_groups-FONTS]] `FONTS`:: Лицензии на шрифты [[licenses-license_name]] === `LICENSE_NAME` и `LICENSE_NAME_NAME` Полное название лицензии. [[licenses-license_name-ex1]] .`LICENSE_NAME` [example] ==== [.programlisting] .... LICENSE= UNRAR LICENSE_NAME= UnRAR License LICENSE_FILE= ${WRKSRC}/license.txt LICENSE_PERMS= dist-mirror dist-sell pkg-mirror pkg-sell auto-accept .... ==== [[licenses-license_file]] === `LICENSE_FILE` и `LICENSE_FILE_NAME` Полный путь к файлу, содержащему текст лицензии, обычно [.filename]#${WRKSRC}/some/file#. Если файл отсутствует в дистрибутиве, а его содержимое слишком длинное для размещения в crossref:makefiles[licenses-license_text,`LICENSE_TEXT`], поместите его в новый файл в [.filename]#${FILESDIR}#. [[licenses-license_file-ex1]] .`LICENSE_FILE` [example] ==== [.programlisting] .... LICENSE= GPLv3+ LICENSE_FILE= ${WRKSRC}/COPYING .... ==== [[licenses-license_text]] === `LICENSE_TEXT` и `LICENSE_TEXT_NAME` Текст для использования в качестве лицензии. Полезно, когда лицензия отсутствует в файлах дистрибутива и её текст краток. [[licenses-license_text-ex1]] .`LICENSE_TEXT` [example] ==== [.programlisting] .... LICENSE= UNKNOWN LICENSE_NAME= unknown LICENSE_TEXT= This program is NOT in public domain.\ It can be freely distributed for non-commercial purposes only,\ and THERE IS NO WARRANTY FOR THIS PROGRAM. LICENSE_PERMS= dist-mirror no-dist-sell pkg-mirror no-pkg-sell auto-accept .... ==== [[licenses-license_distfiles]] === `LICENSE_DISTFILES` и `LICENSE_DISTFILES_NAME` Файлы дистрибутива, к которым применяются лицензии. По умолчанию — все файлы дистрибутива. [[licenses-license_distfiles-ex1]] .`LICENSE_DISTFILES` [example] ==== Используется, когда файлы дистрибутива имеют разные лицензии. Например, один файл имеет лицензию на код, а другой содержит некоторые произведения искусства, которые нельзя распространять: [.programlisting] .... MASTER_SITES= SF/some-game DISTFILES= ${DISTNAME}${EXTRACT_SUFX} artwork.zip LICENSE= BSD3CLAUSE ARTWORK LICENSE_COMB= dual LICENSE_NAME_ARTWORK= The game artwork license LICENSE_TEXT_ARTWORK= The README says that the files cannot be redistributed LICENSE_PERMS_ARTWORK= pkg-mirror pkg-sell auto-accept LICENSE_DISTFILES_BSD3CLAUSE= ${DISTNAME}${EXTRACT_SUFX} LICENSE_DISTFILES_ARTWORK= artwork.zip .... ==== [[licenses-license_comb]] === `LICENSE_COMB` Установите значение `multi`, если применяются все лицензии. Установите значение `dual`, если применяется любая из лицензий. По умолчанию используется значение `single`. [[licenses-license_comb-ex1]] .Двойные лицензии [example] ==== Когда порт содержит указание «Это программное обеспечение может распространяться под GNU General Public License или Artistic License», это означает, что можно использовать любую из этих лицензий. Используйте следующее: [.programlisting] .... LICENSE= ART10 GPLv1 LICENSE_COMB= dual .... Если предоставлены файлы лицензий, используйте это: [.programlisting] .... LICENSE= ART10 GPLv1 LICENSE_COMB= dual LICENSE_FILE_ART10= ${WRKSRC}/Artistic LICENSE_FILE_GPLv1= ${WRKSRC}/Copying .... ==== [[licenses-license_comb-ex2]] .Множественные лицензии [example] ==== Если часть порта имеет одну лицензию, а другая часть — другую, используйте `multi`: [.programlisting] .... LICENSE= GPLv2 LGPL21+ LICENSE_COMB= multi .... ==== [[makefile-portscout]] == `PORTSCOUT` Portscout — это автоматизированная утилита проверки distfile для Коллекции портов FreeBSD, подробно описанная в crossref:keeping-up[distfile-survey,Portscout: сканирование distfile портов FreeBSD]. `PORTSCOUT` определяет специальные условия, в рамках которых работа сканера дистрибутивных файлов Portscout ограничена. Ситуации, когда установлена переменная `PORTSCOUT`, включают: * Когда необходимо игнорировать distfiles для определённых версий. Например, чтобы исключить версию _8.2_ и версию _8.3_ из проверок версий distfiles, так как известно, что они неработоспособны, добавьте: + [.programlisting] .... PORTSCOUT= skipv:8.2,8.3 .... * Когда проверки версий distfile необходимо полностью отключить. Например, если порт больше не будет обновляться, добавьте: + [.programlisting] .... PORTSCOUT= ignore:1 .... * Когда необходимо проверять конкретные версии или определённые мажорные и минорные редакции distfile. Например, если нужно отслеживать только версию _0.6.4_, потому что более новые версии имеют проблемы совместимости с FreeBSD, добавьте: + [.programlisting] .... PORTSCOUT= limit:^0\.6\.4 .... * Когда URL-адреса, перечисляющие доступные версии, отличаются от URL-адресов загрузки. Например, чтобы ограничить проверку версий distfile страницей загрузки для пакета: package:databases/pgtune[] добавьте: + [.programlisting] .... PORTSCOUT= site:http://www.renpy.org/dl/release/ .... [[makefile-depend]] == Зависимости Многие порты зависят от других портов. Это очень удобная особенность большинства Unix-подобных операционных систем, включая FreeBSD. Несколько портов могут использовать общую зависимость вместо того, чтобы включать эту зависимость в каждый порт или пакет, который в ней нуждается. Существует семь переменных, которые можно использовать для обеспечения наличия всех необходимых компонентов на машине пользователя. Также есть предопределённые переменные зависимостей для распространённых случаев и несколько дополнительных для управления поведением зависимостей. [IMPORTANT] ==== Когда у программного обеспечения есть дополнительные зависимости, предоставляющие дополнительные возможности, основные зависимости, перечисленные в `*_DEPENDS`, должны включать те дополнительные зависимости, которые будут полезны большинству пользователей. Основные зависимости никогда не должны быть "минимальным" набором зависимостей. Цель состоит не в том, чтобы включить все возможные зависимости. Включайте только те, которые будут полезны большинству людей. ==== [[makefile-lib_depends]] === `LIB_DEPENDS` Эта переменная определяет разделяемые библиотеки, от которых зависит данный порт. Это список кортежей вида `_lib:dir_`, где `_lib_` — имя разделяемой библиотеки, а `_dir_` — каталог, в котором её следует искать, если она недоступна. Например, [.programlisting] .... LIB_DEPENDS= libjpeg.so:graphics/jpeg .... проверит наличие общей библиотеки jpeg с любой версией и перейдет в подкаталог [.filename]#graphics/jpeg# дерева портов, чтобы собрать и установить её, если она не найдена. Зависимость проверяется дважды: один раз внутри цели `build` и затем внутри цели `install`. Также имя зависимости добавляется в пакет, чтобы `pkg install` (см. man:pkg-install[8]) автоматически установил её, если её нет в системе пользователя. [[makefile-run_depends]] === `RUN_DEPENDS` Эта переменная определяет исполняемые файлы или файлы, от которых зависит порт во время выполнения. Это список кортежей ``_path:dir_``[:``_target_``], где `_path_` — это имя исполняемого файла или файла, _dir_ — каталог, в котором его следует искать, если он недоступен, а _target_ — цель, которую нужно вызвать в этом каталоге. Если _path_ начинается с косой черты (`/`), он считается файлом, и его существование проверяется с помощью `test -e`; в противном случае предполагается, что это исполняемый файл, и `which -s` используется для проверки наличия программы в пути поиска. Например, [.programlisting] .... RUN_DEPENDS= ${LOCALBASE}/news/bin/innd:news/inn \ xmlcatmgr:textproc/xmlcatmgr .... проверит, существует ли файл или каталог [.filename]#/usr/local/news/bin/innd#, и соберет и установит его из подкаталога [.filename]#news/inn# дерева портов, если он не найден. Также будет проверено, находится ли исполняемый файл `xmlcatmgr` в пути поиска, и если он не найден, будет выполнен переход в [.filename]#textproc/xmlcatmgr# для сборки и установки. [NOTE] ==== В этом случае `innd` является исполняемым файлом; если исполняемый файл находится в месте, которое не ожидается в пути поиска, используйте полный путь. ==== [NOTE] ==== Официальный путь поиска `PATH`, используемый в кластере сборки портов [.programlisting] .... /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin .... ==== Зависимость проверяется внутри цели `install`. Также имя зависимости добавляется в пакет, чтобы команда `pkg install` (см. man:pkg-install[8]) автоматически установила её, если она отсутствует в системе пользователя. Часть _target_ может быть опущена, если она совпадает с `DEPENDS_TARGET`. Довольно распространённая ситуация, когда `RUN_DEPENDS` буквально совпадает с `BUILD_DEPENDS`, особенно если портируемое программное обеспечение написано на скриптовом языке или требует одинаковой среды для сборки и выполнения. В этом случае возникает соблазн и интуитивное желание напрямую присвоить одно другому: [.programlisting] .... RUN_DEPENDS= ${BUILD_DEPENDS} .... Однако такое присваивание может загрязнить зависимости во время выполнения записями, не определёнными в оригинальном `BUILD_DEPENDS` порта. Это происходит из-за ленивого вычисления присваивания переменных в man:make[1]. Рассмотрим [.filename]#Makefile# с `USE_*`, которые обрабатываются [.filename]#ports/Mk/bsd.*.mk# для добавления начальных зависимостей сборки. Например, `USES= gmake` добавляет package:devel/gmake[] в `BUILD_DEPENDS`. Чтобы предотвратить попадание таких дополнительных зависимостей в `RUN_DEPENDS`, создайте другую переменную с текущим содержимым `BUILD_DEPENDS` и присвойте её как `BUILD_DEPENDS`, так и `RUN_DEPENDS`: [.programlisting] .... MY_DEPENDS= some:devel/some \ other:lang/other BUILD_DEPENDS= ${MY_DEPENDS} RUN_DEPENDS= ${MY_DEPENDS} .... [IMPORTANT] ==== _Не используйте_ `:=` для присваивания `BUILD_DEPENDS` в `RUN_DEPENDS` или наоборот. Все переменные раскрываются немедленно, что является совершенно неправильным и почти всегда приводит к ошибке. ==== [[makefile-build_depends]] === `BUILD_DEPENDS` Эта переменная указывает исполняемые файлы или файлы, необходимые для сборки данного порта. Как и `RUN_DEPENDS`, это список кортежей ``_path:dir_``[:``_target_``]. Например, [.programlisting] .... BUILD_DEPENDS= unzip:archivers/unzip .... проверит наличие исполняемого файла с именем `unzip` и перейдет в подкаталог [.filename]#archivers/unzip# дерева портов, чтобы собрать и установить его, если он не будет найден. [NOTE] ==== "build" здесь означает все процессы от извлечения до компиляции. Зависимость проверяется внутри цели `extract`. Часть _target_ может быть опущена, если она совпадает с `DEPENDS_TARGET` ==== [[makefile-fetch_depends]] === `FETCH_DEPENDS` Эта переменная определяет исполняемые файлы или файлы, необходимые для загрузки этого порта. Как и предыдущие две, это список кортежей ``_path:dir_``[:``_target_``]. Например, [.programlisting] .... FETCH_DEPENDS= ncftp2:net/ncftp2 .... проверит наличие исполняемого файла с именем `ncftp2` и перейдет в подкаталог [.filename]#net/ncftp2# дерева портов для сборки и установки, если файл не будет найден. Зависимость проверяется внутри цели `fetch`. Часть _target_ может быть опущена, если она совпадает с `DEPENDS_TARGET`. [[makefile-extract_depends]] === `EXTRACT_DEPENDS` Эта переменная указывает исполняемые файлы или файлы, которые требуются для извлечения данного порта. Как и предыдущая, это список кортежей ``_path:dir_``[:``_target_``]. Например, [.programlisting] .... EXTRACT_DEPENDS= unzip:archivers/unzip .... проверит наличие исполняемого файла с именем `unzip` и перейдет в подкаталог [.filename]#archivers/unzip# дерева портов, чтобы собрать и установить его, если он не будет найден. Зависимость проверяется внутри цели `extract`. Часть _target_ может быть опущена, если она совпадает с `DEPENDS_TARGET`. [NOTE] ==== Используйте эту переменную только если извлечение уже не работает (по умолчанию предполагается `tar`) и не может быть исправлено с помощью `USES=tar`, `USES=lha` или `USES=zip`, как описано в crossref:uses[uses,Использование макросов `USES`]. ==== [[makefile-patch_depends]] === `PATCH_DEPENDS` Эта переменная указывает исполняемые файлы или файлы, которые требуются этому порту для применения патчей. Как и предыдущая, это список кортежей ``_path:dir_``[:``_target_``]. Например, [.programlisting] .... PATCH_DEPENDS= ${NONEXISTENT}:java/jfc:extract .... будет спускаться в подкаталог [.filename]#java/jfc# дерева портов для его извлечения. Зависимость проверяется в рамках цели `patch`. Часть _target_ может быть опущена, если она совпадает с `DEPENDS_TARGET`. [[makefile-uses]] === `USES` Параметры могут быть добавлены для определения различных функций и зависимостей, используемых портом. Они указываются путем добавления этой строки в [.filename]#Makefile#: [.programlisting] .... USES= feature[:arguments] .... Для полного списка значений обратитесь к разделу crossref:uses[uses,Использование макросов `USES`]. [WARNING] ==== `USES` нельзя назначать после включения [.filename]#bsd.port.pre.mk#. ==== [[makefile-use-vars]] === `USE_*` Существует несколько переменных для определения общих зависимостей, используемых многими портами. Их использование необязательно, но помогает сократить многословность [.filename]##Makefile## портов. Каждая из них оформлена как `USE_*`. Эти переменные могут использоваться только в [.filename]##Makefile## портов и [.filename]#ports/Mk/bsd.*.mk#. Они не предназначены для настраиваемых пользователем опций — для этой цели используйте `PORT_OPTIONS`. [NOTE] ==== Всегда неправильно устанавливать любые `USE_*` в [.filename]#/etc/make.conf#. Например, установка [.programlisting] .... USE_GCC=X.Y .... (где X.Y — номер версии) добавит зависимость от gccXY для каждого порта, включая сам `lang/gccXY`! ==== [[makefile-use-vars-table]] .`USE_*` [cols="1,1", frame="none", options="header"] |=== | Переменная | Значение |`USE_GCC` -a| +a| Порт требует GCC (`gcc` или `{g-plus-plus}`) для сборки. Некоторые порты нуждаются в определённой, старой версии GCC, другие требуют современных, актуальных версий. Обычно устанавливается в `yes` (означает всегда использовать стабильную, современную версию GCC из портов, согласно `GCC_DEFAULT` в [.filename]#Mk/bsd.default-versions.mk#). Это также значение по умолчанию. Точная версия также может быть указана, например, значением `10`. GCC из базовой системы используется, если он удовлетворяет запрашиваемой версии, в противном случае подходящий компилятор собирается из портов, а `CC` и `CXX` корректируются соответствующим образом. Аргумент `:build`, следующий за указанием версии, добавляет только зависимость во время сборки порта. Например: [example] ==== [.programlisting] .... USE_GCC=yes # порт требует текущей версии GCC USE_GCC=11:build # порт требует GCC 11 только во время сборки .... ==== [NOTE] ==== `USE_GCC=any` устарел и не должен использоваться в новых портах ==== |=== Переменные, связанные с gmake и [.filename]#configure#, описаны в crossref:special[building,Механизмы сборки], тогда как autoconf, automake и libtool описаны в crossref:special[using-autotools,Использование GNU Autotools]. Переменные, связанные с Perl, описаны в crossref:special[using-perl,Использование Perl]. Переменные X11 перечислены в crossref:special[using-x11,Использование X11]. crossref:special[using-gnome,Использование GNOME] посвящено GNOME, а crossref:special[using-kde,Использование KDE] — переменным, связанным с KDE. crossref:special[using-java,Использование Java] документирует переменные Java, тогда как crossref:special[using-php,Веб-приложения, Apache и PHP] содержит информацию о модулях Apache, PHP и PEAR. Python обсуждается в crossref:special[using-python,Использование Python], а Ruby — в crossref:uses[uses-ruby,Использование Ruby]. crossref:special[using-sdl,Использование SDL] предоставляет переменные, используемые для приложений SDL, и, наконец, crossref:special[using-xfce,Использование Xfce] содержит информацию о Xfce. [[makefile-version-dependency]] === Минимальная версия зависимого пакета Минимальная версия зависимого пакета может быть указана в любом `*_DEPENDS`, кроме `LIB_DEPENDS`, используя следующий синтаксис: [.programlisting] .... p5-Spiffy>=0.26:devel/p5-Spiffy .... Первое поле содержит имя зависимого пакета, которое должно соответствовать записи в базе данных пакетов, знак сравнения и версию пакета. Зависимость считается удовлетворённой, если на машине установлен p5-Spiffy-0.26 или новее. [[makefile-note-on-dependencies]] === Заметки о зависимостях Как упомянуто выше, цель по умолчанию, вызываемая при необходимости зависимости, — это `DEPENDS_TARGET`. По умолчанию она установлена в `install`. Это пользовательская переменная; она никогда не определяется в [.filename]#Makefile# порта. Если порту требуется особый способ обработки зависимости, используйте часть `:target` в `*_DEPENDS` вместо переопределения `DEPENDS_TARGET`. При выполнении `make clean` зависимости портов также автоматически очищаются. Если это нежелательно, определите переменную `NOCLEANDEPENDS` в окружении. Это может быть особенно полезно, если среди зависимостей порта есть что-то, что требует много времени для пересборки, например KDE, GNOME или Mozilla. Для безусловной зависимости от другого порта используйте переменную `${NONEXISTENT}` в качестве первого поля `BUILD_DEPENDS` или `RUN_DEPENDS`. Используйте это только в случае, когда необходим исходный код другого порта. Время компиляции можно сократить, указав также цель. Например [.programlisting] .... BUILD_DEPENDS= ${NONEXISTENT}:graphics/jpeg:extract .... всегда будет переходить к порту `jpeg` и извлекать его. [[makefile-circular-dependencies]] === Циклические зависимости фатальны [IMPORTANT] ==== Не создавайте циклических зависимостей в дереве портов! ==== Технология сборки портов не допускает циклических зависимостей. Если такая зависимость будет добавлена, у кого-то в мире почти сразу окажется сломанной установка FreeBSD, а за этим последуют многие другие. Подобные проблемы бывает очень сложно обнаружить. Если есть сомнения, перед внесением изменений обязательно выполните: `cd /usr/ports; make index`. Этот процесс может быть довольно медленным на старых машинах, но он способен избавить множество людей, включая вас, от серьёзных проблем. [[makefile-automatic-dependencies]] === Проблемы, вызванные автоматическими зависимостями Зависимости должны быть объявлены явно или с использованием crossref:makefiles[makefile-options,OPTIONS framework]. Использование других методов, таких как автоматическое обнаружение, усложняет индексацию, что вызывает проблемы для управления портами и пакетами. [[makefile-automatic-dependencies-bad]] .Неправильное объявление необязательной зависимости [example] ==== [.programlisting] .... .include .if exists(${LOCALBASE}/bin/foo) LIB_DEPENDS= libbar.so:foo/bar .endif .... ==== Проблема с попыткой автоматического добавления зависимостей заключается в том, что файлы и настройки за пределами отдельного порта могут измениться в любой момент. Например: индекс строится, затем устанавливается группа портов. Но один из портов устанавливает проверяемый файл. Теперь индекс неверен, потому что у установленного порта неожиданно появилась новая зависимость. Индекс может оставаться неверным даже после пересборки, если другие порты также определяют свою потребность в зависимостях на основе существования других файлов. [[makefile-automatic-dependencies-good]] .Правильное объявление необязательной зависимости [example] ==== [.programlisting] .... OPTIONS_DEFINE= BAR BAR_DESC= Calling cellphones via bar BAR_LIB_DEPENDS= libbar.so:foo/bar .... ==== Проверка переменных опций является правильным методом. Это не вызовет несоответствий в индексе группы портов, при условии что опции были определены до сборки индекса. Затем можно использовать простые скрипты для автоматизации сборки, установки и обновления этих портов и их пакетов. [[makefile-masterdir]] == Подчиненные порты и `MASTERDIR` Если порту необходимо собирать немного разные версии пакетов, используя переменную (например, разрешение или размер бумаги) с разными значениями, создайте по одному подкаталогу для каждого пакета, чтобы пользователям было проще понять, что делать, но старайтесь максимально использовать общие файлы между портами. Обычно, при грамотном использовании переменных, во всех каталогах, кроме одного, требуется лишь очень короткий [.filename]#Makefile#. В единственном [.filename]#Makefile# укажите каталог с остальными файлами с помощью `MASTERDIR`. Также используйте переменную как часть crossref:makefiles[porting-pkgname,`PKGNAMESUFFIX`], чтобы пакеты имели разные имена. Это лучше всего продемонстрировать на примере. Это часть файла [.filename]#print/pkfonts300/Makefile#; [.programlisting] .... PORTNAME= pkfonts${RESOLUTION} PORTVERSION= 1.0 DISTFILES= pk${RESOLUTION}.tar.gz PLIST= ${PKGDIR}/pkg-plist.${RESOLUTION} .if !defined(RESOLUTION) RESOLUTION= 300 .else .if ${RESOLUTION} != 118 && ${RESOLUTION} != 240 && \ ${RESOLUTION} != 300 && ${RESOLUTION} != 360 && \ ${RESOLUTION} != 400 && ${RESOLUTION} != 600 .BEGIN: @${ECHO_MSG} "Error: invalid value for RESOLUTION: \"${RESOLUTION}\"" @${ECHO_MSG} "Possible values are: 118, 240, 300, 360, 400 and 600." @${FALSE} .endif .endif .... Пакет `package:print/pkfonts300[]` также содержит все обычные исправления, файлы пакетов и т.д. При запуске `make` в этом месте будет взято значение разрешения по умолчанию (300), и порт будет собран в обычном режиме. Что касается других разрешений, это _полный_ [.filename]#print/pkfonts360/Makefile#: [.programlisting] .... RESOLUTION= 360 MASTERDIR= ${.CURDIR}/../pkfonts300 .include "${MASTERDIR}/Makefile" .... ([.filename]#print/pkfonts118/Makefile#, [.filename]#print/pkfonts600/Makefile# и все остальные аналогичны). Определение `MASTERDIR` указывает [.filename]#bsd.port.mk#, что стандартный набор подкаталогов, таких как `FILESDIR` и `SCRIPTDIR`, следует искать в [.filename]#pkfonts300#. Строка `RESOLUTION=360` переопределит строку `RESOLUTION=300` в [.filename]#pkfonts300/Makefile#, и порт будет собран с разрешением, установленным на 360. [[makefile-manpages]] == Страницы Справочника Если порт размещает дерево man в другом месте, отличном от `PREFIX`, используйте `MANDIRS` для указания этих каталогов. Обратите внимание, что файлы, соответствующие страницам руководства, должны быть добавлены в [.filename]#pkg-plist# вместе с остальными файлами. Назначение `MANDIRS` — обеспечить автоматическое сжатие страниц руководства, поэтому имена файлов имеют суффикс [.filename]#.gz#. [[makefile-info]] == Файлы информации Если пакету требуется установить файлы GNU info, перечислите их в `INFO` (без завершающего `.info`), по одному документу на строку. Предполагается, что эти файлы будут установлены в [.filename]#PREFIX/INFO_PATH#. Измените `INFO_PATH`, если пакет использует другое расположение. Однако это не рекомендуется. Эти записи содержат только путь относительно [.filename]#PREFIX/INFO_PATH#. Например, пакет package:lang/gcc34[] устанавливает файлы info в [.filename]#PREFIX/INFO_PATH/gcc34#, и `INFO` будет выглядеть примерно так: [.programlisting] .... INFO= gcc34/cpp gcc34/cppinternals gcc34/g77 ... .... Соответствующий код установки/удаления будет автоматически добавлен во временный файл [.filename]#pkg-plist# перед регистрацией пакета. [[makefile-options]] == Параметры Makefile Многие приложения могут быть собраны с дополнительными или различными конфигурациями. Примеры включают выбор естественного (человеческого) языка, графический интерфейс или командная строка, тип поддерживаемой базы данных. Пользователям может потребоваться конфигурация, отличная от стандартной, поэтому система портов предоставляет хуки, которые автор порта может использовать для управления вариантом сборки. Правильная поддержка этих опций сделает пользователей счастливыми и эффективно предоставит два или более порта по цене одного. [[makefile-options-options]] === `OPTIONS` [[makefile-options-background]] ==== Пояснения `OPTIONS_*` предоставляют пользователю, устанавливающему порт, диалоговое окно с доступными опциями, после чего сохраняют выбранные опции в [.filename]#${PORT_DBDIR}/${OPTIONS_NAME}/options#. При следующей сборке порта эти опции будут использованы повторно. `PORT_DBDIR` по умолчанию имеет значение [.filename]#/var/db/ports#. `OPTIONS_NAME` соответствует имени порта (origin) с заменой разделителя на подчёркивания, например, для package:dns/bind99[] это будет `dns_bind99`. Когда пользователь запускает `make config` (или впервые запускает `make build`), система проверяет наличие файла [.filename]#${PORT_DBDIR}/${OPTIONS_NAME}/options#. Если этот файл не существует, используются значения `OPTIONS_*`, и отображается диалоговое окно, где можно включить или отключить опции. Затем файл [.filename]#options# сохраняется, а настроенные переменные используются при сборке порта. Если новая версия порта добавляет новые `OPTIONS`, пользователю будет показан диалог с сохранёнными значениями старых `OPTIONS`, заполненными заранее. `make showconfig` показывает сохранённую конфигурацию. Используйте `make rmconfig` для удаления сохранённой конфигурации. [[makefile-options-syntax]] ==== Синтаксис `OPTIONS_DEFINE` содержит список `OPTIONS`, которые будут использоваться. Они независимы друг от друга и не сгруппированы: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 .... После определения `OPTIONS` описываются (необязательно, но настоятельно рекомендуется): [.programlisting] .... OPT1_DESC= Describe OPT1 OPT2_DESC= Describe OPT2 OPT3_DESC= Describe OPT3 OPT4_DESC= Describe OPT4 OPT5_DESC= Describe OPT5 OPT6_DESC= Describe OPT6 .... [.filename]#ports/Mk/bsd.options.desc.mk# содержит описания для многих распространённых `OPTIONS`. Хотя они часто полезны, переопределите их, если описание недостаточно для порта. [TIP] ==== При описании параметров рассматривайте их с точки зрения пользователя: «Какую функциональность это изменяет?» и «Зачем мне включать этот параметр?» Не просто повторяйте название. Например, описание параметра `NLS` как «включить поддержку NLS» не помогает пользователю, который уже видит название параметра, но может не знать, что оно означает. Описание вроде «Поддержка родного языка с помощью утилиты gettext» гораздо полезнее. ==== [IMPORTANT] ==== Названия параметров всегда пишутся в верхнем регистре. Они не могут использовать смешанный регистр или нижний регистр. ==== `OPTIONS` могут быть сгруппированы как переключаемые варианты, где допускается только один выбор из каждой группы: [.programlisting] .... OPTIONS_SINGLE= SG1 OPTIONS_SINGLE_SG1= OPT3 OPT4 .... [WARNING] ==== В каждый момент времени _должна_ быть выбрана одна опция из каждой группы `OPTIONS_SINGLE`, чтобы параметры были действительными. Один вариант из каждой группы _должен_ быть добавлен в `OPTIONS_DEFAULT`. ==== `OPTIONS` могут быть сгруппированы как переключаемые варианты, где ни один или только один вариант из каждой группы разрешён: [.programlisting] .... OPTIONS_RADIO= RG1 OPTIONS_RADIO_RG1= OPT7 OPT8 .... `OPTIONS` также могут быть сгруппированы в виде списков "множественного выбора", где _хотя бы одна_ опция должна быть включена: [.programlisting] .... OPTIONS_MULTI= MG1 OPTIONS_MULTI_MG1= OPT5 OPT6 .... `OPTIONS` также могут быть сгруппированы в виде списков "множественного выбора", где ни одна или любые опции могут быть включены: [.programlisting] .... OPTIONS_GROUP= GG1 OPTIONS_GROUP_GG1= OPT9 OPT10 .... `OPTIONS` по умолчанию не установлены, если они не перечислены в `OPTIONS_DEFAULT`: [.programlisting] .... OPTIONS_DEFAULT= OPT1 OPT3 OPT6 .... Определения `OPTIONS` должны быть указаны до включения файла [.filename]#bsd.port.options.mk#. Значения `PORT_OPTIONS` можно проверять только после включения [.filename]#bsd.port.options.mk#. Включение [.filename]#bsd.port.pre.mk# также может использоваться и до сих пор широко применяется в портах, написанных до введения [.filename]#bsd.port.options.mk#. Однако следует учитывать, что некоторые переменные не будут работать как ожидается после включения [.filename]#bsd.port.pre.mk#, обычно это некоторые флаги `USE_*`. [[ports-options-simple-use]] .Простое использование `OPTIONS` [example] ==== [.programlisting] .... OPTIONS_DEFINE= FOO BAR OPTIONS_DEFAULT=FOO FOO_DESC= Option foo support BAR_DESC= Feature bar support # Will add --with-foo / --without-foo FOO_CONFIGURE_WITH= foo BAR_RUN_DEPENDS= bar:bar/bar .include .... ==== [[ports-options-check-unset]] .Проверка неустановленных `OPTIONS` порта [example] ==== [.programlisting] .... .if ! ${PORT_OPTIONS:MEXAMPLES} CONFIGURE_ARGS+=--without-examples .endif .... Приведённая выше форма не рекомендуется. Предпочтительный метод — использование параметра configure для фактического включения и отключения функции в соответствии с опцией: [.programlisting] .... # Will add --with-examples / --without-examples EXAMPLES_CONFIGURE_WITH= examples .... ==== [[ports-options-practical-use]] .Пример реального использования `OPTIONS` [example] ==== [.programlisting] .... OPTIONS_DEFINE= EXAMPLES OPTIONS_DEFAULT= PGSQL LDAP SSL OPTIONS_SINGLE= BACKEND OPTIONS_SINGLE_BACKEND= MYSQL PGSQL BDB OPTIONS_MULTI= AUTH OPTIONS_MULTI_AUTH= LDAP PAM SSL EXAMPLES_DESC= Install extra examples MYSQL_DESC= Use MySQL as backend PGSQL_DESC= Use PostgreSQL as backend BDB_DESC= Use Berkeley DB as backend LDAP_DESC= Build with LDAP authentication support PAM_DESC= Build with PAM support SSL_DESC= Build with OpenSSL support # Will add USE_PGSQL=yes PGSQL_USE= pgsql=yes # Will add --enable-postgres / --disable-postgres PGSQL_CONFIGURE_ENABLE= postgres ICU_LIB_DEPENDS= libicuuc.so:devel/icu # Will add --with-examples / --without-examples EXAMPLES_CONFIGURE_WITH= examples # Check other OPTIONS .include .... ==== [[makefile-options-default]] ==== Опции по умолчанию Эти опции всегда включены по умолчанию. * `DOCS` — сборка и установка документации. * `NLS` — Поддержка родного языка. * `EXAMPLES` — сборка и установка примеров. * `IPV6` — Поддержка протокола IPv6. [NOTE] ==== Нет необходимости добавлять их в `OPTIONS_DEFAULT`. Однако, чтобы они были активны и отображались в диалоге выбора опций, их необходимо добавить в `OPTIONS_DEFINE`. ==== [[makefile-options-auto-activation]] === Автоматическая активация функций При использовании скрипта GNU configure следите за тем, какие дополнительные функции активируются автоматическим определением. Явно отключите ненужные дополнительные функции, добавив `--without-xxx` или `--disable-xxx` в `CONFIGURE_ARGS`. [[makefile-options-auto-activation-bad]] .Неправильная обработка опции [example] ==== [.programlisting] .... .if ${PORT_OPTIONS:MFOO} LIB_DEPENDS+= libfoo.so:devel/foo CONFIGURE_ARGS+= --enable-foo .endif .... ==== В приведённом выше примере представьте, что библиотека libfoo установлена в системе. Пользователь не хочет, чтобы это приложение использовало libfoo, поэтому он отключил соответствующую опцию в диалоге `make config`. Однако скрипт configure приложения обнаруживает библиотеку в системе и включает её поддержку в итоговом исполняемом файле. Теперь, когда пользователь решает удалить libfoo из системы, система портов не протестует (зависимость от libfoo не была записана), но приложение перестаёт работать. [[makefile-options-auto-activation-good]] .Правильная обработка опции [example] ==== [.programlisting] .... FOO_LIB_DEPENDS= libfoo.so:devel/foo # Will add --enable-foo / --disable-foo FOO_CONFIGURE_ENABLE= foo .... ==== [NOTE] ==== В некоторых случаях сокращенный синтаксис условных выражений может вызывать проблемы со сложными конструкциями. Ошибки обычно имеют вид `Malformed conditional`, тогда можно использовать альтернативный синтаксис. [.programlisting] .... .if !empty(VARIABLE:MVALUE) .... в качестве альтернативы [.programlisting] .... .if ${VARIABLE:MVALUE} .... ==== [[options-helpers]] === Помощники параметров Существуют макросы, которые помогают упростить условные значения, различающиеся в зависимости от установленных опций. Для удобства приведён полный список: `PLIST_SUB`, `SUB_LIST`:: Для автоматической генерации `%%_OPT_%%` и `%%NO__OPT__%%` см. crossref:makefiles[options_sub, `OPTIONS_SUB`]. + Для более сложных случаев использования см. crossref:makefiles[options-variables, Замена общих переменных, `OPT_VARIABLE` и `OPT_VARIABLE_OFF`]. `CONFIGURE_ARGS`:: Для информации о `--enable-_x_` и `--disable-_x_` см. crossref:makefiles[options-configure_enable, `OPT_CONFIGURE_ENABLE`]. + О `--with-_x_` и `--without-_x_` см. crossref:makefiles[options-configure_with, `OPT_CONFIGURE_WITH`]. + Во всех остальных случаях см. crossref:makefiles[options-configure_on, `OPT_CONFIGURE_ON` и `OPT_CONFIGURE_OFF`]. `CMAKE_ARGS`:: Для аргументов, которые являются булевыми значениями (`on`, `off`, `true`, `false`, `0`, `1`), см. crossref:makefiles[options-cmake_bool, `OPT_CMAKE_BOOL` и `OPT_CMAKE_BOOL_OFF`]. + Для всех остальных случаев см. crossref:makefiles[options-cmake_on, `OPT_CMAKE_ON` и `OPT_CMAKE_OFF`]. `MESON_ARGS`:: Для аргументов, принимающих `true` или `false`, см. crossref:makefiles[options-meson_true, `OPT_MESON_TRUE` и `OPT_MESON_FALSE`]. + Для аргументов, принимающих `yes` или `no`, используйте crossref:makefiles[options-meson_yes, `OPT_MESON_YES` и `OPT_MESON_NO`]. + Для аргументов, принимающих `enabled` или `disabled`, см. crossref:makefiles[options-meson_enabled, `OPT_MESON_ENABLED` и `OPT_MESON_DISABLED`]. + Во всех остальных случаях используйте crossref:makefiles[options-meson_on, `OPT_MESON_ON` и `OPT_MESON_OFF`]. `QMAKE_ARGS`:: См. crossref:makefiles[options-qmake_on, `OPT_QMAKE_ON` и `OPT_QMAKE_OFF`]. `USE_*`:: См. crossref:makefiles[options-use, `OPT_USE` и `OPT_USE_OFF`]. `*_DEPENDS`:: См. crossref:makefiles[options-dependencies, Зависимости, `OPT_DEPTYPE` и `OPT_DEPTYPE_OFF`]. `*` (Любая переменная):: Наиболее используемые переменные имеют своих помощников, см. crossref:makefiles[options-variables, Замена Общих Переменных, `OPT_VARIABLE` и `OPT_VARIABLE_OFF`]. + Для любой переменной без специального помощника см. crossref:makefiles[options-vars, `OPT_VARS` и `OPT_VARS_OFF`]. Зависимости параметров:: Когда для работы опции требуется другая опция, см. crossref:makefiles[options-implies, `OPT_IMPLIES`]. Конфликты опций:: Когда опция не может работать, если включена другая, см. crossref:makefiles[options-prevents, `OPT_PREVENTS` и `OPT_PREVENTS_MSG`]. Цели сборки:: Когда для опции требуется дополнительная обработка, см. crossref:makefiles[options-targets, Дополнительные цели сборки, `_target_-_OPT_-on` и `_target_-_OPT_-off`]. [[options_sub]] ==== `OPTIONS_SUB` Если `OPTIONS_SUB` установлен в `yes`, то каждая из опций, добавленных в `OPTIONS_DEFINE`, будет добавлена в `PLIST_SUB` и `SUB_LIST`, например: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPTIONS_SUB= yes .... эквивалентно: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} PLIST_SUB+= OPT1="" NO_OPT1="@comment " SUB_LIST+= OPT1="" NO_OPT1="@comment " .else PLIST_SUB+= OPT1="@comment " NO_OPT1="" SUB_LIST+= OPT1="@comment " NO_OPT1="" .endif .... [NOTE] ==== Значение `OPTIONS_SUB` игнорируется. Установка любого значения добавит записи `PLIST_SUB` и `SUB_LIST` для _всех_ опций. ==== [[options-use]] ==== `OPT_USE` и `OPT_USE_OFF` Когда выбрана опция _OPT_, для каждой пары `_ключ=значение_` в ``OPT_USE``, _значение_ добавляется к соответствующему `USE_KEY`. Если _значение_ содержит пробелы, замените их запятыми, и они будут преобразованы обратно в пробелы во время обработки. `OPT_USE_OFF` работает аналогично, но когда `OPT` _не_ выбрана. Например: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_USES= xorg OPT1_USE= mysql=yes xorg=x11,xextproto,xext,xrandr OPT1_USE_OFF= openssl=yes .... эквивалентно: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} USE_MYSQL= yes USES+= xorg USE_XORG= x11 xextproto xext xrandr .else USE_OPENSSL= yes .endif .... [[options-configure-helpers]] ==== Помощники `CONFIGURE_ARGS` [[options-configure_enable]] ===== `OPT_CONFIGURE_ENABLE` Когда выбрана опция _OPT_, для каждого _элемента_ в `OPT_CONFIGURE_ENABLE` к `CONFIGURE_ARGS` добавляется `--enable-_элемент_`. Если опция _OPT_ _не_ выбрана, к `CONFIGURE_ARGS` добавляется `--disable-_элемент_`. Необязательный аргумент может быть указан с помощью символа `=`. Этот аргумент добавляется только к опции конфигурации `--enable-_элемент_`. Например: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 OPT1_CONFIGURE_ENABLE= test1 test2 OPT2_CONFIGURE_ENABLE= test2=exhaustive .... эквивалентно: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} CONFIGURE_ARGS+= --enable-test1 --enable-test2 .else CONFIGURE_ARGS+= --disable-test1 --disable-test2 .endif .if ${PORT_OPTIONS:MOPT2} CONFIGURE_ARGS+= --enable-test2=exhaustive .else CONFIGURE_ARGS+= --disable-test2 .endif .... [[options-configure_with]] ===== `OPT_CONFIGURE_WITH` Когда выбрана опция _OPT_, для каждого _элемента_ в `_OPT_CONFIGURE_WITH` к `CONFIGURE_ARGS` добавляется `--with-_элемент_`. Если опция _OPT_ _не_ выбрана, к `CONFIGURE_ARGS` добавляется `--without-_элемент_`. Необязательный аргумент можно указать с помощью символа `=`. Этот аргумент добавляется только к опции конфигурации `--with-_элемент_`. Например: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 OPT1_CONFIGURE_WITH= test1 OPT2_CONFIGURE_WITH= test2=exhaustive .... эквивалентно: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 .include .if ${PORT_OPTIONS:MOPT1} CONFIGURE_ARGS+= --with-test1 .else CONFIGURE_ARGS+= --without-test1 .endif .if ${PORT_OPTIONS:MOPT2} CONFIGURE_ARGS+= --with-test2=exhaustive .else CONFIGURE_ARGS+= --without-test2 .endif .... [[options-configure_on]] ===== `OPT_CONFIGURE_ON` и `OPT_CONFIGURE_OFF` Когда выбрана опция _OPT_, значение `OPT_CONFIGURE_ON`, если оно определено, добавляется к `CONFIGURE_ARGS`. `OPT_CONFIGURE_OFF` работает аналогично, но когда `OPT` _не_ выбрана. Например: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_CONFIGURE_ON= --add-test OPT1_CONFIGURE_OFF= --no-test .... эквивалентно: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} CONFIGURE_ARGS+= --add-test .else CONFIGURE_ARGS+= --no-test .endif .... [TIP] ==== В большинстве случаев помощники crossref:makefiles[options-configure_enable, `OPT_CONFIGURE_ENABLE`] и crossref:makefiles[options-configure_with, `OPT_CONFIGURE_WITH`] предоставляют более короткий и понятный функционал. ==== [[options-cmake-helpers]] ==== Помощники `CMAKE_ARGS` [[options-cmake_on]] ===== `OPT_CMAKE_ON` и `OPT_CMAKE_OFF` Когда выбрана опция _OPT_, значение `OPT_CMAKE_ON`, если оно определено, добавляется к `CMAKE_ARGS`. `OPT_CMAKE_OFF` работает аналогично, но когда `OPT` _не_ выбрана. Например: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_CMAKE_ON= -DTEST:BOOL=true -DDEBUG:BOOL=true OPT1_CMAKE_OFF= -DOPTIMIZE:BOOL=true .... эквивалентно: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} CMAKE_ARGS+= -DTEST:BOOL=true -DDEBUG:BOOL=true .else CMAKE_ARGS+= -DOPTIMIZE:BOOL=true .endif .... [TIP] ==== См. crossref:makefiles[options-cmake_bool, `OPT_CMAKE_BOOL` и `OPT_CMAKE_BOOL_OFF`] для более краткой записи, когда значение является булевым. ==== [[options-cmake_bool]] ===== `OPT_CMAKE_BOOL` и `OPT_CMAKE_BOOL_OFF` Когда выбрана опция _OPT_, для каждого _элемента_ в `OPT_CMAKE_BOOL` добавляется `-D_элемент_:BOOL=true` к `CMAKE_ARGS`. Если опция _OPT_ _не_ выбрана, `-D_элемент_:BOOL=false` добавляется к `CONFIGURE_ARGS`. `OPT_CMAKE_BOOL_OFF` работает наоборот: `-D_элемент_:BOOL=false` добавляется к `CMAKE_ARGS`, когда опция выбрана, и `-D_элемент_:BOOL=true`, когда опция _не_ выбрана. Например: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_CMAKE_BOOL= TEST DEBUG OPT1_CMAKE_BOOL_OFF= OPTIMIZE .... эквивалентно: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} CMAKE_ARGS+= -DTEST:BOOL=true -DDEBUG:BOOL=true \ -DOPTIMIZE:BOOL=false .else CMAKE_ARGS+= -DTEST:BOOL=false -DDEBUG:BOOL=false \ -DOPTIMIZE:BOOL=true .endif .... [[options-meson-helpers]] ==== Помощники `MESON_ARGS` [[options-meson_on]] ===== `OPT_MESON_ON` и `OPT_MESON_OFF` Когда выбрана опция _OPT_, значение `OPT_MESON_ON`, если оно определено, добавляется к `MESON_ARGS`. `OPT_MESON_OFF` работает аналогичным образом, но когда `OPT` _не_ выбрана. Например: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_MESON_ON= -Dopt=1 OPT1_MESON_OFF= -Dopt=2 .... эквивалентно: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} MESON_ARGS+= -Dopt=1 .else MESON_ARGS+= -Dopt=2 .endif .... [[options-meson_true]] ===== `OPT_MESON_TRUE` и `OPT_MESON_FALSE` Когда выбрана опция _OPT_, для каждого _элемента_ в `OPT_MESON_TRUE` добавляется `-D_элемент_=true` в `MESON_ARGS`. Если опция _OPT_ _не_ выбрана, добавляется `-D_элемент_=false` в `MESON_ARGS`. `OPT_MESON_FALSE` работает противоположным образом: `-D_элемент_=false` добавляется в `MESON_ARGS`, когда опция выбрана, и `-D_элемент_=true`, когда опция _не_ выбрана. Например: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_MESON_TRUE= test debug OPT1_MESON_FALSE= optimize .... эквивалентно: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} MESON_ARGS+= -Dtest=true -Ddebug=true \ -Doptimize=false .else MESON_ARGS+= -Dtest=false -Ddebug=false \ -Doptimize=true .endif .... [[options-meson_yes]] ===== `OPT_MESON_YES` и `OPT_MESON_NO` Когда выбрана опция _OPT_, для каждого _элемента_ в `OPT_MESON_YES` добавляется `-D_элемент_=yes` к `MESON_ARGS`. Если опция _OPT_ _не_ выбрана, добавляется `-D_элемент_=no` к `MESON_ARGS`. `OPT_MESON_NO` работает противоположным образом: `-D_элемент_=no` добавляется к `MESON_ARGS`, когда опция выбрана, и `-D_элемент_=yes`, когда опция _не_ выбрана. Например: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_MESON_YES= test debug OPT1_MESON_NO= optimize .... эквивалентно: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} MESON_ARGS+= -Dtest=yes -Ddebug=yes \ -Doptimize=no .else MESON_ARGS+= -Dtest=no -Ddebug=no \ -Doptimize=yes .endif .... [[options-meson_enabled]] ===== `OPT_MESON_ENABLED` и `OPT_MESON_DISABLED` Когда выбрана опция _OPT_, для каждого _элемента_ в `OPT_MESON_ENABLED` добавляется `-D_элемент_=enabled` к `MESON_ARGS`. Когда опция _OPT_ _не_ выбрана, добавляется `-D_элемент_=disabled` к `MESON_ARGS`. `OPT_MESON_DISABLED` работает противоположным образом: `-D_элемент_=disabled` добавляется к `MESON_ARGS`, когда опция выбрана, и `-D_элемент_=enabled`, когда опция _не_ выбрана. Например: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_MESON_ENABLED= test OPT1_MESON_DISABLED= debug .... эквивалентно: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} MESON_ARGS+= -Dtest=enabled -Ddebug=disabled .else MESON_ARGS+= -Dtest=disabled -Ddebug=enabled .endif .... [[options-qmake_on]] ==== `OPT_QMAKE_ON` и `OPT_QMAKE_OFF` Когда выбрана опция _OPT_, значение `OPT_QMAKE_ON`, если оно определено, добавляется к `QMAKE_ARGS`. `OPT_QMAKE_OFF` работает аналогичным образом, но когда `OPT` _не_ выбрана. Например: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_QMAKE_ON= -DTEST:BOOL=true OPT1_QMAKE_OFF= -DPRODUCTION:BOOL=true .... эквивалентно: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} QMAKE_ARGS+= -DTEST:BOOL=true .else QMAKE_ARGS+= -DPRODUCTION:BOOL=true .endif .... [[options-implies]] ==== `OPT_IMPLIES` Предоставляет способ добавления зависимостей между опциями. При выборе _OPT_ все перечисленные в этой переменной опции также будут выбраны. В качестве примера можно использовать описанный ранее crossref:makefiles[options-configure_enable,`OPT_CONFIGURE_ENABLE`]: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 OPT1_IMPLIES= OPT2 OPT1_CONFIGURE_ENABLE= opt1 OPT2_CONFIGURE_ENABLE= opt2 .... Эквивалентно: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 .include .if ${PORT_OPTIONS:MOPT1} CONFIGURE_ARGS+= --enable-opt1 .else CONFIGURE_ARGS+= --disable-opt1 .endif .if ${PORT_OPTIONS:MOPT2} || ${PORT_OPTIONS:MOPT1} CONFIGURE_ARGS+= --enable-opt2 .else CONFIGURE_ARGS+= --disable-opt2 .endif .... [[options-implies-ex1]] .Простое использование `OPT_IMPLIES` [example] ==== Этот порт имеет опцию `X11` и опцию `GNOME`, для сборки которой необходимо выбрать опцию `X11`. [.programlisting] .... OPTIONS_DEFINE= X11 GNOME OPTIONS_DEFAULT= X11 X11_USES= xorg X11_USE= xorg=xi,xextproto GNOME_USE= gnome=gtk30 GNOME_IMPLIES= X11 .... ==== [[options-prevents]] ==== `OPT_PREVENTS` и `OPT_PREVENTS_MSG` Предоставляет способ добавления конфликтов между опциями. Когда выбрана _OPT_, все опции, перечисленные в `OPT_PREVENTS`, должны быть сняты. Если задано `OPT_PREVENTS_MSG` и возникает конфликт, его содержимое будет показано с объяснением причины конфликта. Например: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 OPT1_PREVENTS= OPT2 OPT1_PREVENTS_MSG= OPT1 and OPT2 enable conflicting options .... Примерно эквивалентно: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 .include .if ${PORT_OPTIONS:MOPT2} && ${PORT_OPTIONS:MOPT1} BROKEN= Option OPT1 conflicts with OPT2 (select only one) .endif .... Единственное отличие заключается в том, что первый вариант выведет ошибку после выполнения `make config`, предлагая изменить выбранные настройки. [[options-prevents-ex1]] .Простое использование `OPT_PREVENTS` [example] ==== Этот порт имеет опции `X509` и `SCTP`. Обе опции добавляют патчи, но патчи конфликтуют друг с другом, поэтому их нельзя выбрать одновременно. [.programlisting] .... OPTIONS_DEFINE= X509 SCTP SCTP_PATCHFILES= ${PORTNAME}-6.8p1-sctp-2573.patch.gz:-p1 SCTP_CONFIGURE_WITH= sctp X509_PATCH_SITES= http://www.roumenpetrov.info/openssh/x509/:x509 X509_PATCHFILES= ${PORTNAME}-7.0p1+x509-8.5.diff.gz:-p1:x509 X509_PREVENTS= SCTP X509_PREVENTS_MSG= X509 and SCTP patches conflict .... ==== [[options-vars]] ==== `OPT_VARS` и `OPT_VARS_OFF` Предоставляет универсальный способ установки и добавления значений переменным. [WARNING] ==== Перед использованием `OPT_VARS` и `OPT_VARS_OFF` проверьте, доступен ли более специфичный вспомогательный инструмент в crossref:makefiles[options-variables, Универсальная замена переменных, `OPT_VARIABLE` и `OPT_VARIABLE_OFF`]. ==== Когда выбрана опция _OPT_ и определены `OPT_VARS`, пары `_key_=_value_` и `_key_+=_value_` обрабатываются из `OPT_VARS`. Оператор `=` приводит к перезаписи существующего значения `KEY`, а `+=` добавляет к значению. `OPT_VARS_OFF` работает аналогично, но когда `OPT` _не_ выбрана. [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 OPT3 OPT1_VARS= also_build+=bin1 OPT2_VARS= also_build+=bin2 OPT3_VARS= bin3_build=yes OPT3_VARS_OFF= bin3_build=no MAKE_ARGS= ALSO_BUILD="${ALSO_BUILD}" BIN3_BUILD="${BIN3_BUILD}" .... эквивалентно: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 MAKE_ARGS= ALSO_BUILD="${ALSO_BUILD}" BIN3_BUILD="${BIN3_BUILD}" .include .if ${PORT_OPTIONS:MOPT1} ALSO_BUILD+= bin1 .endif .if ${PORT_OPTIONS:MOPT2} ALSO_BUILD+= bin2 .endif .if ${PORT_OPTIONS:MOPT2} BIN3_BUILD= yes .else BIN3_BUILD= no .endif .... [IMPORTANT] ==== Значения, содержащие пробелы, должны быть заключены в кавычки: [.programlisting] .... OPT_VARS= foo="bar baz" .... Это связано с тем, как man:make[1] обрабатывает пробелы при раскрытии переменных. Когда `OPT_VARS= foo=bar baz` раскрывается, переменная в итоге содержит две строки: `foo=bar` и `baz`. Однако отправитель, вероятно, предполагал, что должна быть только одна строка — `foo=bar baz`. Заключение значения в кавычки предотвращает использование пробела в качестве разделителя. Также _не_ добавляйте лишние пробелы после знака `_var_=` и перед значением, это также разобьёт строку на две части. _Это не сработает_: [.programlisting] .... OPT_VARS= foo= bar .... ==== [[options-dependencies]] ==== Зависимости, `OPT_DEPTYPE` и `OPT_DEPTYPE_OFF` Для любого из этих типов зависимостей: * `PKG_DEPENDS` * `EXTRACT_DEPENDS` * `PATCH_DEPENDS` * `FETCH_DEPENDS` * `BUILD_DEPENDS` * `LIB_DEPENDS` * `RUN_DEPENDS` Когда выбрана опция _OPT_, значение `OPT_DEPTYPE`, если оно определено, добавляется к `DEPTYPE`. `OPT_DEPTYPE_OFF` работает аналогично, но когда _не_ выбрана `OPT`. Например: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_LIB_DEPENDS= liba.so:devel/a OPT1_LIB_DEPENDS_OFF= libb.so:devel/b .... эквивалентно: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} LIB_DEPENDS+= liba.so:devel/a .else LIB_DEPENDS+= libb.so:devel/b .endif .... [[options-variables]] ==== Универсальная замена переменных, `OPT_VARIABLE` и `OPT_VARIABLE_OFF` Для любой из этих переменных: * `ALL_TARGET` * `BINARY_ALIAS` * `BROKEN` * `CATEGORIES` * `CFLAGS` * `CONFIGURE_ENV` * `CONFLICTS` * `CONFLICTS_BUILD` * `CONFLICTS_INSTALL` * `CPPFLAGS` * `CXXFLAGS` * `DESKTOP_ENTRIES` * `DISTFILES` * `EXTRACT_ONLY` * `EXTRA_PATCHES` * `GH_ACCOUNT` * `GH_PROJECT` * `GH_SUBDIR` * `GH_TAGNAME` * `GH_TUPLE` * `GL_ACCOUNT` * `GL_COMMIT` * `GL_PROJECT` * `GL_SITE` * `GL_SUBDIR` * `GL_TUPLE` * `IGNORE` * `INFO` * `INSTALL_TARGET` * `LDFLAGS` * `LIBS` * `MAKE_ARGS` * `MAKE_ENV` * `MASTER_SITES` * `PATCHFILES` * `PATCH_SITES` * `PLIST_DIRS` * `PLIST_FILES` * `PLIST_SUB` * `PORTDOCS` * `PORTEXAMPLES` * `SUB_FILES` * `SUB_LIST` * `TEST_TARGET` * `USES` Когда выбрана опция _OPT_, значение `OPT_ABOVEVARIABLE`, если оно определено, добавляется к `_ABOVEVARIABLE_`. `OPT_ABOVEVARIABLE_OFF` работает аналогично, но когда `OPT` _не_ выбрана. Например: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_USES= gmake OPT1_CFLAGS_OFF= -DTEST .... эквивалентно: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} USES+= gmake .else CFLAGS+= -DTEST .endif .... [NOTE] ==== Некоторые переменные отсутствуют в этом списке, в частности `PKGNAMEPREFIX` и `PKGNAMESUFFIX`. Это сделано намеренно. Порт _не должен_ изменять своё имя при изменении набора опций. ==== [WARNING] ==== Некоторые из этих переменных, по крайней мере `ALL_TARGET`, `DISTFILES` и `INSTALL_TARGET`, получают свои значения по умолчанию _после_ обработки опций. С такими строками в [.filename]#Makefile#: [.programlisting] .... ALL_TARGET= all DOCS_ALL_TARGET= doc .... Если опция `DOCS` включена, `ALL_TARGET` будет иметь конечное значение `all doc`; если опция отключена, значение будет `all`. Только со строкой помощника опций в [.filename]#Makefile#: [.programlisting] .... DOCS_ALL_TARGET= doc .... Если опция `DOCS` включена, `ALL_TARGET` будет иметь окончательное значение `doc`; если опция отключена, значение будет `all`. ==== [[options-targets]] ==== Дополнительные цели сборки, `_target_-_OPT_-on` и `_target_-_OPT_-off` Эти цели в [.filename]#Makefile# могут принимать дополнительные опциональные цели сборки: * `pre-fetch` * `do-fetch` * `post-fetch` * `pre-extract` * `do-extract` * `post-extract` * `pre-patch` * `do-patch` * `post-patch` * `pre-configure` * `do-configure` * `post-configure` * `pre-build` * `do-build` * `post-build` * `pre-install` * `do-install` * `post-install` * `post-stage` * `pre-package` * `do-package` * `post-package` Когда выбрана опция _OPT_, цель `_TARGET_-_OPT_-on`, если она определена, выполняется после `_TARGET_`. `_TARGET_-_OPT_-off` работает аналогично, но когда `OPT` _не_ выбрана. Например: [.programlisting] .... OPTIONS_DEFINE= OPT1 post-patch-OPT1-on: @${REINPLACE_CMD} -e '/opt1/s|/usr/bin/|${EXAMPLESDIR}/|' ${WRKSRC}/Makefile post-patch-OPT1-off: @${REINPLACE_CMD} -e '/opt1/s|/usr/bin/|${PREFIX}/bin/|' ${WRKSRC}/Makefile .... эквивалентно: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include post-patch: .if ${PORT_OPTIONS:MOPT1} @${REINPLACE_CMD} -e '/opt1/s|/usr/bin/|${EXAMPLESDIR}/|' ${WRKSRC}/Makefile .else @${REINPLACE_CMD} -e '/opt1/s|/usr/bin/|${PREFIX}/bin/|' ${WRKSRC}/Makefile .endif .... [[makefile-wrkdir]] == Указание рабочего каталога Каждый порт извлекается в рабочий каталог, который должен быть доступен для записи. Система портов по умолчанию распаковывает `DISTFILES` в каталог с именем `${DISTNAME}`. Другими словами, если в [.filename]#Makefile# указано: [.programlisting] .... PORTNAME= foo DISTVERSION= 1.0 .... то файлы дистрибутива порта содержат каталог верхнего уровня [.filename]#foo-1.0#, и остальные файлы находятся в этом каталоге. Если нужно расположение файлов в других каталогах, можно переопределить ряд переменных. [[makefile-wrksrc]] === `WRKSRC` Переменная указывает имя каталога, который создаётся при распаковке distfiles приложения. Чтобы в нашем предыдущем примере распаковка происходила в каталог с именем [.filename]#foo# (а не [.filename]#foo-1.0#), напишите: [.programlisting] .... WRKSRC= ${WRKDIR}/foo .... или можно [.programlisting] .... WRKSRC= ${WRKDIR}/${PORTNAME} .... [[makefile-wrksrc_subdir]] === `WRKSRC_SUBDIR` Если исходные файлы, необходимые для порта, находятся в подкаталоге распакованного дистрибутива, присвойте `WRKSRC_SUBDIR` имя этого каталога. [.programlisting] .... WRKSRC_SUBDIR= src .... [[makefile-no_wrksubdir]] === `NO_WRKSUBDIR` Если порт не распаковывается в подкаталог вообще, установите `NO_WRKSUBDIR`, чтобы указать это. [.programlisting] .... NO_WRKSUBDIR= yes .... [NOTE] ==== Поскольку `WRKDIR` является единственным каталогом, который должен быть доступен для записи во время сборки, и используется для хранения многих файлов, фиксирующих состояние сборки, извлечение порта будет принудительно выполнено в подкаталог. ==== [[conflicts]] == Обработка конфликтов Существует три различные переменные для регистрации конфликтов между пакетами и портами: `CONFLICTS`, `CONFLICTS_INSTALL` и `CONFLICTS_BUILD`. [NOTE] ==== Эти переменные автоматически устанавливают переменную `IGNORE`, более подробно описанную в crossref:porting-dads[dads-noinstall,Пометка порта как неустанавливаемого с помощью `BROKEN`, `FORBIDDEN` или `IGNORE`]. ==== При удалении одного из нескольких конфликтующих портов рекомендуется оставлять `CONFLICTS` в тех других портах на несколько месяцев, чтобы учесть пользователей, которые обновляются лишь время от времени. [[conclicts-conflicts_install]] `CONFLICTS_INSTALL`:: Если пакет не может сосуществовать с другими пакетами (из-за конфликтов файлов, несовместимости во время выполнения и т.д.). Проверка `CONFLICTS_INSTALL` выполняется после этапа сборки и перед этапом установки. [[conclicts-conflicts_build]] `CONFLICTS_BUILD`:: Если порт не может быть собран, когда уже установлены другие определённые порты. Конфликты сборки не фиксируются в результирующем пакете. [[conclicts-conflicts]] `CONFLICTS`:: Если порт не может быть собран, когда определённый порт уже установлен и итоговый пакет не может сосуществовать с другим пакетом. Проверка `CONFLICTS` выполняется до этапа сборки и до этапа установки. Каждый элемент, разделённый пробелами, в значениях переменных `CONFLICTS*` сопоставляется с пакетами(кроме того, который собирается) с использованием правил раскрытия шаблонов имён файлов в оболочке shell. Это позволяет перечислить все варианты порта в списке конфликтов вместо необходимости исключать собираемый вариант из этого списка. Например, если установлен git-lite, `CONFLICTS_INSTALL=git git-lite` позволит выполнить: [source, shell] .... % make -C devel/git FLAVOR=lite all deinstall install .... Но следующая команда сообщит о конфликте, так как установленное имя базового пакета — `git-lite`, а `git` будет собран, но не может быть установлен вместе с `git-lite`: [source, shell] .... % make -C devel/git FLAVOR=default all deinstall install .... Без этой функции Makefile потребовал бы по одному `_flavor__CONFLICTS_INSTALL` для каждого варианта, перечисляя все остальные варианты. Наиболее распространённым содержимым одной из этих переменных является база пакета другого порта. База пакета — это имя пакета без указания версии, её можно получить, выполнив команду `make -V PKGBASE`. [[conflicts-ex1]] .Простой пример использования `CONFLICTS*` [example] ==== Пакет package:dns/bind99[] не может быть установлен, если присутствует пакет package:dns/bind910[], так как они устанавливают одинаковые файлы. Сначала соберите базовый пакет для использования: [source, shell] .... % make -C dns/bind99 -V PKGBASE bind99 % make -C dns/bind910 -V PKGBASE bind910 .... Затем добавьте в [.filename]#Makefile# пакета package:dns/bind99[]: [.programlisting] .... CONFLICTS_INSTALL= bind910 .... И добавьте в [.filename]#Makefile# пакета package:dns/bind910[]: [.programlisting] .... CONFLICTS_INSTALL= bind99 .... ==== Иногда только определённые версии другого порта несовместимы. В этом случае используйте полное имя пакета, включая версию. При необходимости используйте подстановочные символы шаблонов имён файлов оболочки, такие как `*` и `?`, чтобы охватить все необходимые версии. [[conflicts-ex2]] .Использование `CONFLICTS*` с шаблонами имён файлов. [example] ==== В версиях с 2.0 по 2.4.1_2 пакет package:deskutils/gnotime[] устанавливал встроенную версию пакета package:databases/qof[]. Чтобы отразить это прошлое, [.filename]#Makefile# пакета package:databases/qof[] содержит: [.programlisting] .... CONFLICTS_INSTALL= gnotime-2.[0-3]* \ gnotime-2.4.0* gnotime-2.4.1 \ gnotime-2.4.1_[12] .... Первый элемент соответствует версиям `2.0`–`2.3`, второй — всем редакциям `2.4.0`, третий — точно версии `2.4.1`, а последний — первой и второй редакциям версии `2.4.1`. package:deskutils/gnotime[] не имеет строки конфликтов, потому что его текущая версия не конфликтует ни с чем другим. ==== Переменная `DISABLE_CONFLICTS` может быть временно установлена при выполнении целей, на которые не влияют конфликты. Эту переменную не следует устанавливать в Makefiles портов. [source, shell] .... % make -DDISABLE_CONFLICTS patch .... [[install]] == Установка файлов [IMPORTANT] ==== Фаза `install` очень важна для конечного пользователя, так как она добавляет файлы в его систему. Все дополнительные команды, выполняемые в целях `*-install` [.filename]#Makefile# порта, должны выводиться на экран. _Не_ заглушайте эти команды с помощью `@` или `.SILENT`. ==== [[install-macros]] === Макросы `INSTALL_*` Используйте макросы, предоставленные в [.filename]#bsd.port.mk#, чтобы обеспечить корректные режимы файлов в целях `*-install` порта. Устанавливайте владельца напрямую в [.filename]#pkg-plist# в соответствующих записях, таких как `@(_владелец_,_группа_,)`, `@owner _владелец_` и `@group _группа_`. Эти операторы действуют до переопределения или до конца [.filename]#pkg-plist#, поэтому не забудьте сбросить их, когда они больше не нужны. Владелец по умолчанию — `root:wheel`. Дополнительную информацию см. в crossref:plist[plist-keywords-base,Базовые Ключевые Слова]. * `INSTALL_PROGRAM` — команда для установки бинарных исполняемых файлов. * `INSTALL_SCRIPT` — команда для установки исполняемых скриптов. * `INSTALL_LIB` — это команда для установки общих библиотек (но не статических библиотек). * `INSTALL_KLD` — это команда для установки загружаемых модулей ядра. Некоторые архитектуры не поддерживают удаление символов из модулей, поэтому используйте эту команду вместо `INSTALL_PROGRAM`. * `INSTALL_DATA` — это команда для установки общих данных, включая статические библиотеки. * `INSTALL_MAN` — это команда для установки man-страниц и другой документации (она ничего не сжимает). Эти переменные передаются команде man:install[1] с соответствующими флагами для каждой ситуации. [IMPORTANT] ==== Не используйте `INSTALL_LIB` для установки статических библиотек, так как их удаление делает их бесполезными. Вместо этого используйте `INSTALL_DATA`. ==== [[install-strip]] === Удаление символов из бинарных файлов и разделяемых библиотек Установленные бинарные файлы должны быть очищены от отладочной информации. Не очищайте бинарные файлы вручную, если это не является абсолютно необходимым. Макрос `INSTALL_PROGRAM` устанавливает и очищает бинарный файл одновременно. Макрос `INSTALL_LIB` делает то же самое с разделяемыми библиотеками. Когда файл необходимо очистить, но ни макросы `INSTALL_PROGRAM`, ни `INSTALL_LIB` не подходят, `${STRIP_CMD}` очищает программу или разделяемую библиотеку. Обычно это делается в цели `post-install`. Например: [.programlisting] .... post-install: ${STRIP_CMD} ${STAGEDIR}${PREFIX}/bin/xdl .... Когда необходимо удалить отладочную информацию из нескольких файлов: [.programlisting] .... post-install: .for l in geometry media body track world ${STRIP_CMD} ${STAGEDIR}${PREFIX}/lib/lib${PORTNAME}-${l}.so.0 .endfor .... Используйте man:file[1] для файла, чтобы определить, был ли он подвергнут удалению символов. man:file[1] сообщает, что бинарные файлы либо `stripped` (удалены символы), либо `not stripped` (символы не удалены). Кроме того, man:strip[1] обнаружит программы, которые уже были подвергнуты удалению символов, и завершит работу без ошибок. [IMPORTANT] ==== Когда определён `WITH_DEBUG`, elf-файлы _не должны_ быть очищены. Переменные (`STRIP_CMD`, `INSTALL_PROGRAM`, `INSTALL_LIB`, ...) и crossref:uses[uses,`USES`], предоставляемые фреймворком, обрабатывают это автоматически. Некоторое программное обеспечение добавляет `-s` к своим `LDFLAGS`. В этом случае либо удалите `-s`, если установлен `WITH_DEBUG`, либо удалите его безусловно и используйте `STRIP_CMD` в `post-install`. ==== [[install-copytree]] === Установка целого дерева файлов Иногда необходимо установить большое количество файлов с сохранением их иерархической структуры. Например, копирование всего дерева каталогов из `WRKSRC` в целевой каталог под `PREFIX`. Обратите внимание, что `PREFIX`, `EXAMPLESDIR`, `DATADIR` и другие переменные путей всегда должны предваряться `STAGEDIR` для соблюдения процедуры промежуточной установки (см. crossref:special[staging,Промежуточная установка]). Для этой ситуации существуют два макроса. Преимущество использования этих макросов вместо `cp` заключается в том, что они гарантируют целевым файлам правильные значения владельца и разрешений. Первый макрос, `COPYTREE_BIN`, устанавливает все установленные файлы как исполняемые, что делает его подходящим для установки в [.filename]#PREFIX/bin#. Второй макрос, `COPYTREE_SHARE#, не устанавливает исполняемые разрешения для файлов и, следовательно, подходит для установки файлов в [.filename]#PREFIX/share#. [.programlisting] .... post-install: ${MKDIR} ${STAGEDIR}${EXAMPLESDIR} (cd ${WRKSRC}/examples && ${COPYTREE_SHARE} . ${STAGEDIR}${EXAMPLESDIR}) .... Этот пример установит содержимое каталога [.filename]#examples# из дистрибутива вендора в соответствующее расположение примеров порта. [.programlisting] .... post-install: ${MKDIR} ${STAGEDIR}${DATADIR}/summer (cd ${WRKSRC}/temperatures && ${COPYTREE_SHARE} "June July August" ${STAGEDIR}${DATADIR}/summer) .... И этот пример установит данные летних месяцев в подкаталог [.filename]#summer# каталога [.filename]#DATADIR#. Дополнительные аргументы `find` могут быть переданы через третий аргумент макросов `COPYTREE_*`. Например, чтобы установить все файлы из первого примера, кроме Makefiles, можно использовать следующие команды. [.programlisting] .... post-install: ${MKDIR} ${STAGEDIR}${EXAMPLESDIR} (cd ${WRKSRC}/examples && \ ${COPYTREE_SHARE} . ${STAGEDIR}${EXAMPLESDIR} "! -name Makefile") .... Эти макросы не добавляют установленные файлы в [.filename]#pkg-plist#. Их необходимо добавлять вручную. Для дополнительной документации (`PORTDOCS`, см. crossref:makefiles[install-documentation, Установка дополнительной документации]) и примеров (`PORTEXAMPLES`), префиксы `%%PORTDOCS%%` или `%%PORTEXAMPLES%%` должны быть добавлены в [.filename]#pkg-plist#. [[install-documentation]] === Установка дополнительной документации Если у программного обеспечения есть документация, помимо стандартных страниц man и info, которая может быть полезна пользователю, установите её в `DOCSDIR`. Это можно сделать, как и в предыдущем пункте, в цели `post-install`. Создайте новый каталог для порта. Имя каталога — `DOCSDIR`. Обычно оно равно `PORTNAME`. Однако, если пользователю может потребоваться установка разных версий порта одновременно, можно использовать полное имя `PKGNAME`. Поскольку устанавливаются только файлы, перечисленные в [.filename]#pkg-plist#, можно безопасно всегда устанавливать документацию в `STAGEDIR` (см. crossref:special[staging,Staging]). Поэтому блоки `.if` требуются только в тех случаях, когда устанавливаемые файлы достаточно велики, чтобы вызвать значительные накладные расходы на ввод-вывод. [.programlisting] .... post-install: ${MKDIR} ${STAGEDIR}${DOCSDIR} ${INSTALL_DATA} ${WRKSRC}/docs/xvdocs.ps ${STAGEDIR}${DOCSDIR} .... С другой стороны, если в порте есть опция DOCS, установите документацию в цели `post-install-DOCS-on`. Эти цели описаны в crossref:makefiles[options-targets, Дополнительные цели сборки, `_target_-_OPT_-on` и `_target_-_OPT_-off`]. Вот несколько полезных переменных и их стандартное раскрытие при использовании в [.filename]#Makefile#: * `DATADIR` раскрывается в [.filename]#PREFIX/share/PORTNAME#. * `DATADIR_REL` раскрывается в [.filename]#share/PORTNAME#. * `DOCSDIR` раскрывается в [.filename]#PREFIX/share/doc/PORTNAME#. * `DOCSDIR_REL` раскрывается в [.filename]#share/doc/PORTNAME#. * `EXAMPLESDIR` раскрывается в [.filename]#PREFIX/share/examples/PORTNAME#. * `EXAMPLESDIR_REL` раскрывается в [.filename]#share/examples/PORTNAME#. [NOTE] ==== Опция `DOCS` управляет только дополнительной документацией, устанавливаемой в `DOCSDIR`. Она не применяется к стандартным man-страницам и info-страницам. Содержимое, устанавливаемое в `EXAMPLESDIR`, контролируется опцией `EXAMPLES`. ==== Эти переменные экспортируются в `PLIST_SUB`. Их значения будут представлены там в виде путей относительно [.filename]#PREFIX#, если это возможно. То есть, [.filename]#share/doc/PORTNAME# будет заменено на `%%DOCSDIR%%` в списке упаковки по умолчанию и так далее. (Подробнее о подстановках в [.filename]#pkg-plist# см. crossref:plist[plist-sub,здесь].) Все условно устанавливаемые файлы и каталоги документации включаются в [.filename]#pkg-plist# с префиксом `%%PORTDOCS%%`, например: [.programlisting] .... %%PORTDOCS%%%%DOCSDIR%%/AUTHORS %%PORTDOCS%%%%DOCSDIR%%/CONTACT .... В качестве альтернативы перечислению файлов документации в [.filename]#pkg-plist#, порт может установить переменную `PORTDOCS` в список имён файлов и шаблонов имён файлов shell для добавления в итоговый список упаковки. Имена будут относительны к `DOCSDIR`. Поэтому порт, использующий `PORTDOCS` и нестандартное расположение документации, должен соответствующим образом установить `DOCSDIR`. Если в `PORTDOCS` указан каталог или он соответствует шаблону из этой переменной, всё поддерево содержащихся файлов и каталогов будет зарегистрировано в итоговом списке упаковки. Если опция `DOCS` отключена, файлы и каталоги, перечисленные в `PORTDOCS`, не будут установлены или добавлены в список упаковки порта. Установка документации в `PORTDOCS`, как показано выше, остаётся на усмотрение самого порта. Типичный пример использования `PORTDOCS`: [.programlisting] .... PORTDOCS= README.* ChangeLog docs/* .... [NOTE] ==== Эквивалентами `PORTDOCS` для файлов, установленных в `DATADIR` и `EXAMPLESDIR`, являются `PORTDATA` и `PORTEXAMPLES` соответственно. Содержимое файла [.filename]#pkg-message# отображается при установке. Подробности см. в разделе crossref:pkg-files[porting-message,использование файла [.filename]#pkg-message#]. Файл [.filename]#pkg-message# не нужно добавлять в [.filename]#pkg-plist#. ==== [[install-subdirs]] === Подкаталоги в `PREFIX` Попробуйте сделать так, чтобы порт размещал файлы в правильных подкаталогах `PREFIX`. Некоторые порты собирают всё в кучу и помещают в подкаталог с именем порта, что неверно. Также многие порты размещают все файлы, кроме бинарников, заголовочных файлов и страниц руководства, в подкаталоге [.filename]#lib#, что плохо согласуется с парадигмой BSD. Многие из этих файлов должны быть перемещены в один из следующих каталогов: [.filename]#etc# (файлы настройки/конфигурации), [.filename]#libexec# (исполняемые файлы для внутреннего использования), [.filename]#sbin# (исполняемые файлы для суперпользователей/администраторов), [.filename]#info# (документация для браузера info) или [.filename]#share# (архитектурно-независимые файлы). Подробности см. в man:hier[7]; правила, действующие для [.filename]#/usr#, в основном применимы и к [.filename]#/usr/local#. Исключение составляют порты, связанные с USENET "news". Они могут использовать [.filename]#PREFIX/news# в качестве места назначения для своих файлов. [[binary-alias]] == Использование `BINARY_ALIAS` для переименования команд вместо исправления сборки Когда определена переменная `BINARY_ALIAS`, будут созданы символьные ссылки на указанные команды в каталоге, который будет добавлен в начало переменной `PATH`. Используйте это для замены жёстко заданных команд, от которых зависит этап сборки, без необходимости исправлять какие-либо файлы сборки. [[binary-alias-ex1]] .Использование `BINARY_ALIAS` для предоставления `gsed` в качестве `sed` [example] ==== Некоторые порты ожидают, что `sed` будет вести себя как GNU sed и используют возможности, которые man:sed[1] не предоставляет. GNU sed доступен в пакете package:textproc/gsed[] на FreeBSD. Используйте `BINARY_ALIAS` для замены `sed` на `gsed` на время сборки: [.programlisting] .... BUILD_DEPENDS= gsed:textproc/gsed ... BINARY_ALIAS= sed=gsed .... ==== [[binary-alias-ex2]] .Использование `BINARY_ALIAS` для создания псевдонимов жёстко заданных команд `python3` [example] ==== Порт, в котором есть жёсткая ссылка на `python3` в скриптах сборки, требует его наличия в `PATH` во время сборки. Используйте `BINARY_ALIAS` для создания псевдонима, указывающего на нужный бинарный файл Python 3: [.programlisting] .... USES= python:3.4+,build ... BINARY_ALIAS= python3=${PYTHON_CMD} .... См. crossref:special[using-python,Использование Python] для получения дополнительной информации о `USES=python`. ==== [NOTE] ==== Бинарные псевдонимы создаются после обработки зависимостей, указанных через `BUILD_DEPENDS` и `LIB_DEPENDS`, но до цели `configure`. Это приводит к различным ограничениям. Например, программы, установленные через `TEST_DEPENDS`, нельзя использовать для создания бинарного псевдонима, так как тестовые зависимости, указанные таким образом, обрабатываются после создания бинарных псевдонимов. ==== diff --git a/documentation/content/ru/books/porters-handbook/pkg-files/_index.adoc b/documentation/content/ru/books/porters-handbook/pkg-files/_index.adoc index 44394d1bd5..6563eda6eb 100644 --- a/documentation/content/ru/books/porters-handbook/pkg-files/_index.adoc +++ b/documentation/content/ru/books/porters-handbook/pkg-files/_index.adoc @@ -1,322 +1,322 @@ --- description: 'Хитрости с файлами pkg-*' next: books/porters-handbook/testing params: path: /books/porters-handbook/pkg-files/ prev: books/porters-handbook/plist showBookMenu: 'true' tags: ["pkg", "pkg-message", "UCL", "pkg-install", "pkg-deinstall"] title: 'Глава 9. Файлы pkg-*' weight: 9 --- [[pkg-files]] = pkg-* :doctype: book :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumoffset: 9 :partnums: :source-highlighter: rouge :experimental: :images-path: books/porters-handbook/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] Есть несколько приёмов работы с файлами [.filename]#pkg-*#, которые мы ещё не описали, но они иногда могут быть очень кстати. [[porting-message]] == pkg-message Если вам нужно вывести сообщение для человека, устанавливающего приложение, то вы можете поместить сообщение в файл [.filename]#pkg-message#. Эта возможность часто оказывается полезной для вывода дополнительных шагов установки, которые нужно предпринять после выполнения команды `pkg install`, или для вывода информации о лицензировании. [IMPORTANT] ==== * Файл [.filename]#pkg-message# должен содержать только информацию, _критически важную_ для настройки и работы в FreeBSD, а также уникальную для данного порта. * Информация по настройке должна отображаться только при первоначальной установке. Инструкции по обновлению должны показываться только при обновлении с соответствующей версии. * Не обрамляйте сообщения ни пробелами, ни строками символов (такими как `----------`, `**********` или `==========`). Оставьте форматирование man:pkg[8]. * Коммиттеры имеют полное право ограничивать существующие сообщения диапазонами установки или обновления, используя спецификации формата UCL. * Пожалуйста, убедитесь, что используете соответствующие инструменты для управления службами. ** Используйте `service имя start` для запуска службы вместо `/usr/local/etc/rc.d/имя start` ** Используйте `sysrc name_enable=YES` для изменения параметров в rc.conf ==== pkg-message поддерживает два формата: raw:: Обычный текстовый файл. Его сообщение отображается только при установке. UCL:: Если файл начинается с символа "`[`", то он считается файлом в формате UCL. Формат UCL описан на странице https://github.com/vstakhov/libucl[libucl's GitHub page]. [NOTE] ==== Не добавляйте запись для [.filename]#pkg-message# в [.filename]#pkg-plist#. ==== [[porting-message-ucl]] === UCL в pkg-message Формат следующий. Это должен быть массив объектов. Сами объекты могут содержать следующие ключевые слова: `message`:: Отображаемое сообщение. Этот ключевой параметр является обязательным. `type`:: Когда сообщение должно быть отображено. `maximum_version`:: Только если `type` имеет значение `upgrade`. Отображается, если обновление выполняется с версии строго ниже указанной. `minimum_version`:: Только если `type` имеет значение `upgrade`. Отображается, если обновление выполняется с версии, строго большей, чем указанная. Ключевые слова `maximum_version` и `minimum_version` можно комбинировать. Ключевое слово `type` может иметь три значения: `install`:: Сообщение должно отображаться только при установке пакета. `remove`:: Сообщение должно отображаться только при удалении пакета. `upgrade`:: сообщение должно отображаться только во время обновления пакета. [IMPORTANT] ==== Для сохранения совместимости с файлами [.filename]#pkg-message#, не использующими UCL, первая строка UCL [.filename]#pkg-message# _ДОЛЖНА быть_ одиночным символом "`[`", а последняя строка _ДОЛЖНА быть_ одиночным символом "`]`". ==== [[porting-message-ucl-short-ex]] .Короткие строки UCL [example] ==== Сообщение ограничено двойными кавычками `"`, это используется для простых однострочных строк: [.programlisting] .... [ { type: install message: "Simple message" } ] .... ==== [[porting-message-ucl-multiline-ex]] .Многострочные строки UCL [example] ==== Многострочные строки используют стандартную нотацию heredoc. Разделитель многострочной строки _должен_ начинаться сразу после символов `<<` без пробелов и _должен_ состоять только из заглавных букв. Чтобы завершить многострочную строку, добавьте строку-разделитель на отдельной строке без пробелов. Сообщение из раздела crossref:pkg-files[porting-message-ucl-short-ex,Короткие строки UCL] может быть записано как: [.programlisting] .... [ { type: install message: < 1.0 and < 3.0 remove that file." } ] .... [IMPORTANT] -**** +===== При отображении сообщения во время обновления важно ограничить случаи, когда оно показывается пользователю. Чаще всего это делается с помощью `maximum_version`, чтобы ограничить его использование обновлениями до определённой версии, когда требуется выполнить конкретное действие. -**** +===== ==== [[pkg-install]] == pkg-install, pkg-pre-install и pkg-post-install Если порту необходимо выполнять команды при установке бинарного пакета с помощью `pkg add` или `pkg install`, используйте [.filename]#pkg-install#. Он запускается дважды через `pkg`: первый раз как `${SH} pkg-install ${PKGNAME} PRE-INSTALL` перед установкой пакета и второй раз как `${SH} pkg-install ${PKGNAME} POST-INSTALL` после его установки. Переменная `$2` может быть проверена, чтобы определить, в каком режиме выполняется скрипт. Переменная окружения `PKG_PREFIX` устанавливается равной имени каталога установки пакета. Если используется [.filename]#pkg-pre-install# или [.filename]#pkg-post-install#, скрипт выполняется только один раз (до или после установки пакета), с единственным аргументом `${PKGNAME}`. Использование [.filename]#pkg-pre-install.lua# или [.filename]#pkg-post-install.lua# запускает скрипт на Lua вместо shell-скрипта. Скрипты на Lua, выполняемые `pkg`, предоставляют некоторые расширения и несколько ограничений, которые описаны в man:pkg-lua-script[5]. [NOTE] ==== Использование [.filename]#pkg-pre-install# (или [.filename]#pkg-pre-install.lua#) и [.filename]#pkg-post-install# (или [.filename]#pkg-post-install.lua#) предпочтительнее, чем использование [.filename]#pkg-install#. ==== Эти скрипты автоматически добавляются в список упаковки. [IMPORTANT] ==== Эти скрипты предназначены для упрощения настройки пакетов после установки. Они _не должны_ использоваться для запуска служб, остановки служб или выполнения любых других команд, которые изменяют текущую работающую систему. ==== [[pkg-deinstall]] == pkg-deinstall, pkg-pre-deinstall и pkg-post-deinstall Эти скрипты выполняются при удалении пакета. Скрипт [.filename]#pkg-deinstall# выполняется дважды командой `pkg delete`. Первый раз как `${SH} pkg-deinstall ${PKGNAME} DEINSTALL` до удаления порта и второй раз как `${SH} pkg-deinstall ${PKGNAME} POST-DEINSTALL` после удаления порта. Переменная `$2` может быть проверена для определения режима, в котором выполняется скрипт. Переменная окружения `PKG_PREFIX` устанавливается в каталог установки пакета. Если используется [.filename]#pkg-pre-deinstall# или [.filename]#pkg-post-deinstall#, скрипт выполняется только один раз (до или после удаления пакета) с единственным аргументом `${PKGNAME}`. Использование [.filename]#pkg-pre-deinstall.lua# или [.filename]#pkg-post-deinstall.lua# запустит скрипт на Lua вместо shell-скрипта. Скрипты на Lua, выполняемые `pkg`, предоставляют некоторые расширения и ограничения, которые описаны в man:pkg-lua-script[5]. [NOTE] ==== Использование [.filename]#pkg-pre-deinstall# (или [.filename]#pkg-pre-deinstall.lua#) и [.filename]#pkg-post-deinstall# (или [.filename]#pkg-post-deinstall.lua#) предпочтительнее, чем использование [.filename]#pkg-deinstall#. ==== Эти скрипты автоматически добавляются в список упаковки. [IMPORTANT] ==== Эти скрипты предназначены для упрощения очистки после удаления пакетов. Они _не должны_ использоваться для запуска служб, остановки служб или выполнения любых других команд, которые изменяют текущую работающую систему. ==== [[pkg-names]] == Изменение имён файлов [.filename]#pkg-*# Все имена файлов [.filename]#pkg-\*# определяются с помощью переменных, так что вы можете изменить их, если это нужно, в вашем файле [.filename]#Makefile#. Это особенно полезно, если вы используете одни и те же файлы [.filename]#pkg-*# совместно между несколькими портами или пишете в один из вышеперечисленных файлов (в главе о crossref:porting-dads[porting-wrkdir,"записи в каталоги, отличные от ``WRKDIR``"] объяснено, почему не рекомендуется осуществлять запись непосредственно в файлы [.filename]#pkg-*#). Вот список имён переменных и их значений по умолчанию. (Значение `PKGDIR` по умолчанию равно `${MASTERDIR}`.) [.informaltable] [cols="1,1", frame="none", options="header"] |=== | Переменная | Значение по умолчанию |`DESCR` |`${PKGDIR}/pkg-descr` |`PLIST` |`${PKGDIR}/pkg-plist` |`PKGINSTALL` |`${PKGDIR}/pkg-install` |`PKGPREINSTALL` |`${PKGDIR}/pkg-pre-install` |`PKGPOSTINSTALL` |`${PKGDIR}/pkg-post-install` |`PKGDEINSTALL` |`${PKGDIR}/pkg-deinstall` |`PKGPREDEINSTALL` |`${PKGDIR}/pkg-pre-deinstall` |`PKGPOSTDEINSTALL` |`${PKGDIR}/pkg-post-deinstall` |`PKGMESSAGE` |`${PKGDIR}/pkg-message` |=== [[using-sub-files]] == Использование `SUB_FILES` и `SUB_LIST` Переменные `SUB_FILES` и `SUB_LIST` подходят для задания в файлах порта динамических значений, таких как `PREFIX` установки в [.filename]#pkg-message#. В переменной `SUB_FILES` указывается перечень файлов для автоматического изменения. Каждый _file_ из перечня `SUB_FILES` обязан иметь соответствующий [.filename]#file.in#, присутствующий в `FILESDIR`. Измененная версия будет создана в `WRKDIR`. Файлы, определённые в качестве значения `USE_RC_SUBR` (или устаревшего `USE_RCORDER`), автоматически добавляются в `SUB_FILES`. Для файлов [.filename]#pkg-message#, [.filename]#pkg-install# и [.filename]#pkg-deinstall# устанавливается соответствующая переменная Makefile, указывающая на обработанную версию. Переменная `SUB_LIST` содержит перечень пар `VAR=VALUE`. В каждом файле из `SUB_FILES` для каждой пары будет произведена замена `%%VAR%%` на `VALUE`. Некоторые общие пары определяются автоматически: `PREFIX`, `LOCALBASE`, `DATADIR`, `DOCSDIR`, `EXAMPLESDIR`, `WWWDIR` и `ETCDIR`. Любая строка, начинающаяся с `@comment`, будет удалена из конечного файла после подстановки переменной. В следующем примере в [.filename]#pkg-message# будет сделана замена `%%ARCH%%` на системную архитектуру: [.programlisting] .... SUB_FILES= pkg-message SUB_LIST= ARCH=${ARCH} .... Обратите внимание, что в этом примере в `FILESDIR` обязательно существование файла [.filename]#pkg-message.in#. Пример хорошего [.filename]#pkg-message.in#: [.programlisting] .... Now it is time to configure this package. Copy %%PREFIX%%/shared/examples/putsy/%%ARCH%%.conf into your home directory as .putsy.conf and edit it. .... diff --git a/documentation/content/ru/books/porters-handbook/quick-porting/_index.adoc b/documentation/content/ru/books/porters-handbook/quick-porting/_index.adoc index 86971e94da..e62592e754 100644 --- a/documentation/content/ru/books/porters-handbook/quick-porting/_index.adoc +++ b/documentation/content/ru/books/porters-handbook/quick-porting/_index.adoc @@ -1,278 +1,278 @@ --- description: 'Как быстро создать новый порт FreeBSD' next: books/porters-handbook/slow-porting params: path: /books/porters-handbook/quick-porting/ prev: books/porters-handbook/new-port showBookMenu: 'true' tags: ["quick porting", "guide", "port", "ports collection", "how-to"] title: 'Глава 3. Быстрое портирование' weight: 3 --- [[quick-porting]] = Быстрое портирование :doctype: book :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumoffset: 3 :partnums: :source-highlighter: rouge :experimental: :images-path: books/porters-handbook/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] В этом разделе описано, как быстро создать новый порт. Для случаев, когда этот быстрый метод не подходит, полный процесс "Медленного портирования" описан в разделе crossref:slow-porting[slow-porting,Медленное портирование]. Во-первых, скачайте оригинальный tar-файл и поместите его в каталог `DISTDIR`, который по умолчанию есть не что иное, как [.filename]#/usr/ports/distfiles#. [NOTE] ==== Здесь предполагается, что программное обеспечение компилируется без проблем как есть, то есть для работы приложения на вашей системе FreeBSD не потребовалось абсолютно никаких изменений. Если требовалось что-то изменить, то вам придется обратиться также и к следующему разделу — crossref:slow-porting[slow-porting,Медленное портирование]. ==== [NOTE] ==== Перед началом портирования рекомендуется установить переменную man:make[1] `DEVELOPER` в [.filename]#/etc/make.conf#. [source, shell] .... # echo DEVELOPER=yes >> /etc/make.conf .... Эта настройка включает "режим разработчика", в котором отображаются предупреждения при использовании устаревших конструкций и задействуются некоторые дополнительные проверки при вызове команды `make`. ==== [[porting-makefile]] == Создание файла [.filename]#Makefile# Минимальный [.filename]#Makefile# будет выглядеть примерно так: [.programlisting] .... PORTNAME= oneko DISTVERSION= 1.1b CATEGORIES= games MASTER_SITES= ftp://ftp.rediris.es/sites/ftp.freebsd.org/pub/FreeBSD/ MAINTAINER= youremail@example.com COMMENT= Cat chasing a mouse all over the screen WWW= http://www.daidouji.com/oneko/ .include .... Посмотрим, сможете ли вы его понять. Более подробный пример приведен в секции crossref:porting-samplem[porting-samplem,пример Makefile]. [[porting-desc]] == Создание информационных файлов Имеется два информационных файла, которые требуются для любого порта, вне зависимости от того, является ли он пакетом или нет. Это [.filename]#pkg-descr# и [.filename]#pkg-plist#. Префикс [.filename]#pkg-# отличает их от других файлов. [[porting-pkg-descr]] === [.filename]#pkg-descr# Это более подробное краткое описание порта. От одного до нескольких абзацев, кратко описывающих, что представляет собой порт, будет достаточно. [NOTE] ==== Это _не_ руководство и не подробное описание того, как использовать или компилировать порт! _Пожалуйста, будьте осторожны при копировании из [.filename]#README# или manpage_. Очень часто они не содержат краткого описания порта или имеют неудобный формат. Например, manpages используют выравнивание по ширине, что особенно плохо выглядит с моноширинными шрифтами. С другой стороны, содержимое файла [.filename]#pkg-descr# должно быть длиннее, чем crossref:makefiles[makefile-comment,строка `COMMENT` из Makefile]. Оно должно более подробно объяснять, что собой представляет данный порт. ==== Хорошо составленный [.filename]#pkg-descr# описывает порт достаточно полно, чтобы пользователю не приходилось сверяться с документацией или посещать вебсайт для понимания того, что делает данное программное обеспечение, чем оно может быть полезно или какие хорошие функции у него имеются. Упоминание про определённые требования, такие как используемый графический инструментарий, тяжёлые зависимости, окружение для запуска или используемый язык программирования помогут пользователям определиться, будет ли этот порт для них работать. [NOTE] ==== URL, который ранее включался в последнюю строку файла [.filename]#pkg-descr#, был перемещен в [.filename]#Makefile#. ==== [[porting-pkg-plist]] === [.filename]#pkg-plist# Здесь перечисляются все файлы, устанавливаемые портом. Его также называют "списком для упаковки", потому что пакет генерируется упаковкой файлов, которые здесь указаны. Имена путей указываются относительно установочного префикса (обычно [.filename]#/usr/local#). Вот маленький пример: [.programlisting] .... bin/oneko man/man1/oneko.1.gz lib/X11/app-defaults/Oneko lib/X11/oneko/cat1.xpm lib/X11/oneko/cat2.xpm lib/X11/oneko/mouse.xpm .... Обратитесь к странице справочной системы по команде man:pkg-create[8] с подробным описанием формата списка упаковки. [NOTE] ==== Рекомендуется, чтобы имена файлов в этом списке были отсортированы в алфавитном порядке. Это позволит значительно облегчить сверку изменений при обновлении порта. Фреймворк делает это корректно, когда список пакета crossref:plist[plist-autoplist,сгенерирован автоматически]. ==== [TIP] ==== Создание списка упаковки вручную может оказаться весьма трудоёмкой задачей. Если порт устанавливает большое количество файлов, раздел об crossref:plist[plist-autoplist,автоматическом построении списка упаковки] может помочь сэкономить время. ==== Существует только одно исключение, когда у порта может отсутствовать [.filename]#pkg-plist#. Если порт устанавливает лишь несколько файлов, а возможно, и каталогов, то они могут быть перечислены в переменных `PLIST_FILES` и `PLIST_DIRS`, соответственно, внутри файла [.filename]#Makefile# порта. К примеру, мы можем обойтись без файла [.filename]#pkg-plist# у приведённого выше порта [.filename]#oneko#, добавив следующие строки в [.filename]#Makefile#: [.programlisting] .... PLIST_FILES= bin/oneko \ man/man1/oneko.1.gz \ lib/X11/app-defaults/Oneko \ lib/X11/oneko/cat1.xpm \ lib/X11/oneko/cat2.xpm \ lib/X11/oneko/mouse.xpm .... [NOTE] ==== Использование `PLIST_FILES` не должно быть чрезмерным. При поиске происхождения файла обычно просматривают файлы [.filename]#pkg-plist# в дереве портов с помощью grep. Указание файлов в `PLIST_FILES` в [.filename]#Makefile# усложняет этот поиск. ==== [TIP] ==== Если порту требуется создать пустой каталог или он создаёт каталоги вне [.filename]#${PREFIX}# во время установки, обратитесь к разделу crossref:plist[plist-dir-cleaning,Очистка пустых каталогов] для получения дополнительной информации. ==== [TIP] ==== Поскольку `PLIST_FILES` является переменной man:make[1], любая запись с пробелами должна быть заключена в кавычки. Например, при использовании ключевых слов, описанных в man:pkg-create[8] и crossref:plist[plist-keywords,Расширение списка пакетов с помощью ключевых слов], запись должна быть заключена в кавычки. [.programlisting] .... PLIST_FILES= "@sample ${ETCDIR}/oneko.conf.sample" .... ==== Позже мы увидим, как [.filename]#pkg-plist# и `PLIST_FILES` могут использоваться для выполнения crossref:plist[plist,более сложных задач]. [[porting-checksum]] == Создание файла с контрольной суммой Просто введите команду `make makesum`. Правила утилиты make автоматически сгенерируют файл [.filename]#distinfo#. Не пытайтесь создавать этот файл вручную. [[porting-testing]] == Тестирование порта Вы должны удостовериться, что правила построения порта выполняют именно то, что вы хотите, включая создание пакета для порта. Вот те важные вещи, которые вы должны проверить: * [.filename]#pkg-plist# не содержит ничего сверх того, что устанавливается портом. * [.filename]#pkg-plist# содержит абсолютно все, что устанавливается портом. * Порт может быть установлен с помощью указания цели `install`. Это позволяет убедиться в правильной работе сценария установки. * Порт может быть правильным образом удалён с помощью указания цели `deinstall`. Это позволяет убедиться в правильной работе сценария удаления. * Порт имеет доступ к сетевым ресурсам только во время фазы цели `fetch`. Это важно для сборщиков пакетов, таких как package:ports-mgmt/poudriere[]. * Убедитесь, что команду `make package` можно выполнить от имени обычного пользователя (то есть не от `root`). Если это не работает, возможно, потребуется исправить программное обеспечение. См. также crossref:uses[uses-fakeroot,`fakeroot`] и crossref:uses[uses-uidfix,`uidfix`]. [.procedure] .*Procedure: Рекомендуемый порядок проверки* . `make stage` . `make check-orphans` . `make package` . `make install` . `make deinstall` . `make package` (как пользователь) Убедитесь, что на любом из этапов не выдаётся никаких предупреждений. Тщательное автоматизированное тестирование можно выполнить с помощью package:ports-mgmt/poudriere[] из коллекции портов, дополнительную информацию см. в crossref:testing[testing-poudriere,poudriere]. Он поддерживает `клетки`, в которых можно протестировать все указанные выше шаги без воздействия на состояние основной системы. [[porting-portlint]] == Проверка вашего порта утилитой `portlint` Будьте добры, пользуйтесь утилитой `portlint` для проверки того, что ваш порт соответствует нашим рекомендациям. Программа package:ports-mgmt/portlint[] является частью Коллекции Портов. В частности, вы можете захотеть проверить, правильно ли сформирован файл crossref:porting-samplem[porting-samplem,Makefile] и соответствующим ли образом именован crossref:porting-pkgname[porting-pkgname,пакет]. [IMPORTANT] ==== Не следуйте слепо выводу `portlint`. Это статический инструмент для проверки, и иногда он ошибается. ==== [[porting-submitting]] == Посылка нового порта Перед посылкой нового порта прочитайте раздел о том, что crossref:porting-dads[porting-dads,можно и нельзя] делать. Когда вы наконец довольны своим первым портом, единственное, что осталось сделать, это включить его в основное дерево портов FreeBSD и осчастливить этим всех остальных. [IMPORTANT] ==== Нам не нужны каталог [.filename]#work# или пакет [.filename]#pkgname.txz#, поэтому их можно удалить. ==== Далее создайте файл man:patch[1]. Предположим, что порт называется `oneko` и находится в категории `games`. [[porting-submitting-diff]] .Создание [.filename]#.diff# для нового порта [example] ==== Добавьте все файлы с помощью `git add .`, затем просмотрите изменения с помощью `git diff`. Например: [source, shell] .... % git add . % git diff --staged .... Убедитесь, что все необходимые файлы включены, затем зафиксируйте изменение в вашей локальной ветке и создайте патч с помощью `git format-patch` [source, shell] .... % git commit % git format-patch origin/main .... Патч, созданный с помощью `git format-patch`, будет содержать идентификатор автора и адреса электронной почты, что упрощает применение разработчиками (с помощью `git am`) и правильное указание авторства. [IMPORTANT] -**** +==== Чтобы упростить применение патча для коммиттеров в их рабочей копии дерева портов, пожалуйста, создайте файл [.filename]#.diff# из корня вашего дерева портов. -**** +==== ==== Отправьте файл [.filename]#oneko.diff# через https://bugs.freebsd.org/submit/[форму отправки отчётов об ошибках]. Укажите продукт "Ports & Packages", компонент "Individual Port(s)" и следуйте приведённым там инструкциям. Добавьте краткое описание программы в поле Description PR (например, сокращённую версию `COMMENT`) и не забудьте прикрепить файл [.filename]#oneko.diff#. [NOTE] ==== Хорошее описание в заголовке сообщения о проблеме значительно облегчает работу коммиттеров портов. Для новых портов мы предпочитаем нечто вроде "[NEW PORT] _категория/название_порта краткое описание порта_". Следование этой схеме упрощает и ускоряет начало работы по добавлению нового порта. ==== После отправки порта, пожалуйста, потерпите. Время, необходимое для включения нового порта во FreeBSD, может занимать от нескольких дней до нескольких месяцев. https://bugs.freebsd.org/bugzilla/query.cgi[ Здесь] можно увидеть список ожидающих PR для портов. Чтобы получить список _открытых_ PR для портов, выберите _Open_ и _Ports & Packages_ в форме поиска, затем нажмите btn:[Search]. После ознакомления с новым портом мы ответим, если это необходимо, и добавим его в дерево. Имя отправителя также будет добавлено в список extref:{contributors}[Дополнительных участников FreeBSD, contrib-additional] и другие файлы. [IMPORTANT] ==== Ранее можно было отправлять исправления для новых портов, используя файл man:shar[1]; однако с развитием man:git[1] это больше не актуально. Коммиттеры больше не принимают файлы man:shar[1], так как их использование чревато ошибками и не добавляет соответствующую запись в [.filename]#Makefile# категории. ==== diff --git a/documentation/content/ru/books/porters-handbook/testing/_index.adoc b/documentation/content/ru/books/porters-handbook/testing/_index.adoc index 3a9b0b17cc..6d67be6cd5 100644 --- a/documentation/content/ru/books/porters-handbook/testing/_index.adoc +++ b/documentation/content/ru/books/porters-handbook/testing/_index.adoc @@ -1,539 +1,539 @@ --- description: 'Тестирование порта FreeBSD' next: books/porters-handbook/upgrading params: path: /books/porters-handbook/testing/ prev: books/porters-handbook/pkg-files showBookMenu: 'true' tags: ["testing", "port", "Portclippy", "Portfmt", "Portlint", "poudriere", "sets"] title: 'Глава 10. Тестирование вашего порта' weight: 10 --- [[testing]] = Тестирование порта :doctype: book :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumoffset: 10 :partnums: :source-highlighter: rouge :experimental: :images-path: books/porters-handbook/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] [[make-describe]] == Запуск `make describe` Некоторые утилиты FreeBSD для сопровождения портов, например, man:portupgrade[1], опираются на базу данных с именем [.filename]#/usr/ports/INDEX#, в которой отслеживаются такие характеристики портов, как их зависимости. Файл [.filename]#INDEX# создаётся при помощи [.filename]#ports/Makefile# верхнего уровня по команде `make index`, спускающейся в подкаталог каждого порта и выполняющей в нём `make describe`. Таким образом, если выполнение `make describe` с каким-либо портом завершится неудачно, то никому не удастся создать [.filename]#INDEX#, при этом много людей вскоре станут несчастны. [NOTE] ==== Возможность генерировать этот файл очень важна вне зависимости от того, какие параметры присутствуют в [.filename]#make.conf#, поэтому, пожалуйста, избегайте, таких вещей, как использование декларации `.error`, когда (к примеру) требования к зависимости не было удовлетворено. (Смотрите crossref:porting-dads[dads-dot-error, Избегайте использования конструкции `.error`].) ==== Если `make describe` выдаёт строчку, а не ошибку, то для вас это пройдёт безболезненно. Обратитесь к файлу [.filename]#bsd.port.mk#, чтобы выяснить значение выдаваемых строк. Также обратите внимание, что запуск актуальной версии `portlint` (как указано в следующем разделе) приведёт к автоматическому выполнению `make describe`. [[make-test]] == Запуск `make test` Даже если порт успешно собирается, рекомендуется убедиться, что программа корректно выполняет свои функции. Если исходный проект предоставляет тесты вместе с программным обеспечением, рекомендуется их запустить и проверить, что всё работает, как ожидается. Порт может автоматически включить тесты, используя переменную `TEST_TARGET`. Когда эта переменная установлена, она содержит имя цели тестирования порта. Обычно это просто `test`, но другие варианты включают `tests`, `check` или, в специфических случаях, такие значения, как `run_tests.py`. В дополнение к переменной `TEST_TARGET` фреймворк предоставляет следующие переменные для управления выполнением тестов: * `TEST_WRKSRC` — это каталог для выполнения тестов. * `TEST_ENV` содержит дополнительные переменные, которые передаются на этап тестирования. * `TEST_ARGS` содержит любые дополнительные аргументы, переданные на этапе тестирования. Примеры использования этих переменных можно найти в package:cad/xyce[], package:www/libjwt[] и других. [NOTE] ==== Убедитесь, что тесты не ломаются при обновлении порта. ==== [[testing-portclippy]] == Portclippy / Portfmt Эти инструменты поставляются из пакета:ports-mgmt/portfmt[]. Portclippy — это линтер, проверяющий, расположены ли переменные в файле [.filename]#Makefile# в правильном порядке согласно crossref:order[porting-order,Порядку переменных в Makefile портов]. Portfmt — это инструмент для автоматического форматирования [.filename]#Makefile#. [[testing-portlint]] == Portlint Проверьте свою работу командой crossref:quick-porting[porting-portlint,`portlint`] перед тем, как её отослать или перенести в дерево портов. `portlint` предупреждает вас о многих распространённых ошибках, как функциональных, так и стилистических. Для нового (или скопированного внутри хранилища) порта самым подходящим является запуск `portlint -A`; для уже существующего порта достаточно будет запустить `portlint -C`. Так как для обнаружения ошибок `portlint` использует эвристические методы, то им могут выдаваться и ошибочные предупреждения. Кроме того, время от времени нечто, отмечаемое как некорректность, из-за ограничений механизма создания портов не может быть сделано никак иначе. Если вы сомневаетесь, то лучше всего спросить в {freebsd-ports}. [[testing-porttools]] == Инструменты для работы с портами Программа package:ports-mgmt/porttools[] входит в состав Коллекции Портов. `port` является сценарием переднего плана, который может упростить вам задачу тестирования. Если вы хотите проверить новый порт или обновить существующий, то вы можете использовать `port test` для проверки вашего порта, включая проверку crossref:testing[testing-portlint,`portlint`]. Эта команда также находит и отображает любые файлы, которые невключенные в [.filename]#pkg-plist#. Смотрите следующий пример: [source, shell] .... # port test /usr/ports/net/csup .... [[porting-prefix]] == `PREFIX` и `DESTDIR` Переменная `PREFIX` определяет, куда будет установлен порт. По умолчанию это [.filename]#/usr/local#, но может меняться пользователем на собственный путь, такой как [.filename]#/opt#. В вашем порту значение этой переменной должно учитываться. Если пользователь установил переменную `DESTDIR`, то она определяет полное альтернативное окружение, обычно, это jail или установленная система, смонтированная в месте, отличном от [.filename]#/#. На самом деле порт устанавливается в [.filename]#DESTDIR/PREFIX# и регистрируется в базе данных пакетов в [.filename]#DESTDIR/var/db/pkg#. Поскольку управление `DESTDIR` производится автоматически инфраструктурой портов с помощью man:chroot[8], вам не нужны никакие изменения или проявление особой осторожности при написании портов, совместимых с `DESTDIR`. Значение переменной `PREFIX` будет установлено в `LOCALBASE` (по умолчанию [.filename]#/usr/local#). Если задана переменная `USE_LINUX_PREFIX`, то `PREFIX` примет значение `LINUXBASE` (по умолчанию [.filename]#/compat/linux#). Избегание явно прописываемых путей [.filename]#/usr/local# в исходном коде сделает порт гораздо более гибким и способным удовлетворить потребности других серверов. Часто этого можно добиться простой заменой строк [.filename]#/usr/local# в различных файлах [.filename]#Makefile# внутри порта на `${PREFIX}`. Эта переменная автоматически передаётся далее на каждом этапе построения и установки. Проверьте, что ваше приложение не устанавливает чего-либо в каталог [.filename]#/usr/local# вместо `PREFIX`. Наличие явно указанных путей можно быстро проверить следующим образом: [source, shell] .... % make clean; make package PREFIX=/var/tmp/`make -V PORTNAME` .... Если что-то было установлено за пределами `PREFIX`, то процесс создания пакета сообщит об отсутствии файлов. Это также стоит проверить с использованием поддержки каталога сборки (смотрите crossref:special[staging, Staging]): [source, shell] .... % make stage && make check-plist && make stage-qa && make package .... * `check-plist` проверяет отсутствующие в plist файлы и файлы в plist, которые не установлены портом. * `stage-qa` проверяет наличие распространённых проблем, таких как неправильный шебанг (интерпретаторная строка в первой строке скрипта), символьные ссылки, указывающие за пределы каталога стадии,файлы с setuid битом и библиотеки с отладочной информацией... Эти тесты не обнаружат жёстко заданные пути в файлах порта, а также не проверят, что `LOCALBASE` используется корректно для ссылок на файлы из других портов. Временно установленный порт в [.filename]#/var/tmp/`make -V PORTNAME`# должен быть протестирован на корректную работу, чтобы убедиться в отсутствии проблем с путями. `PREFIX` не должен быть явно установлен в [.filename]#Makefile# порта. Пользователи, устанавливающие порт, могут задать `PREFIX` в другом месте, и порт должен учитывать эту настройку. Обращайтесь к программам и файлам из других портов с помощью упомянутых выше переменных, а не явных путей. Например, если порт требует, чтобы макрос `PAGER` содержал полный путь к `less`, не используйте явный путь [.filename]#/usr/local/bin/less#. Вместо этого используйте `${LOCALBASE}`: [.programlisting] .... -DPAGER=\"${LOCALBASE}/bin/less\" .... Путь с `LOCALBASE` с большей вероятностью продолжит работать, если системный администратор переместил всё дерево [.filename]#/usr/local# в другое место. [TIP] ==== Все эти тесты выполняются автоматически при запуске `poudriere testport` или `poudriere bulk -t`. Настоятельно рекомендуется каждому участнику разработки портов устанавливать и тестировать свои порты с помощью этого инструмента. Дополнительную информацию можно найти в crossref:testing[testing-poudriere, poudriere]. ==== [[testing-poudriere]] == poudriere Для контрибьютора портов poudriere является одним из самых важных и полезных инструментов для тестирования и сборки. Его основные возможности включают: * Массовая сборка всего дерева портов, определённых подмножеств дерева портов или отдельного порта с его зависимостями * Автоматическая упаковка результатов сборки * Генерация файлов журнала сборки для каждого порта * Предоставление подписанного репозитория man:pkg[8] * Тестирование сборки портов перед отправкой патча в трекер ошибок FreeBSD или внесением изменений в дерево портов * Тестирование успешных сборок портов с использованием различных параметров Поскольку poudriere выполняет сборку в чистой среде man:jail[8] и использует возможности man:zfs[8], он имеет несколько преимуществ по сравнению с традиционным тестированием на основной системе: * Отсутствие загрязнения основной среды: никаких оставшихся файлов, случайных удалений или изменений существующих конфигурационных файлов. * Проверяет [.filename]#pkg-plist# на наличие отсутствующих или лишних записей * Коммиттеры портов иногда запрашивают журнал poudriere вместе с отправкой патча, чтобы оценить, готов ли патч для интеграции в дерево портов Также его настройка и использование довольно просты, он не имеет зависимостей и будет работать в любой поддерживаемой версии FreeBSD. В этом разделе показано, как установить, настроить и запустить poudriere в рамках обычного рабочего процесса разработчика портов. Примеры в этом разделе показывают стандартную структуру файлов, принятую в FreeBSD. Внесите соответствующие изменения, если у вас используются другие настройки. Дерево портов, обозначаемое как `${PORTSDIR}`, находится в [.filename]#/usr/ports#. По умолчанию `${LOCALBASE}` и `${PREFIX}` указывают на [.filename]#/usr/local#. [[testing-poudriere-installing]] === Установка poudriere poudriere доступен в дереве портов в пакете package:ports-mgmt/poudriere[]. Его можно установить с помощью man:pkg[8] или из портов: [source, shell] .... # pkg install poudriere .... или [source, shell] .... # make -C /usr/ports/ports-mgmt/poudriere install clean .... Также существует версия poudriere в разработке, которая в конечном итоге станет следующим релизом. Она доступна в пакете:ports-mgmt/poudriere-devel[]. Эта версия используется для официальных сборок пакетов FreeBSD, поэтому она хорошо протестирована. В ней часто появляются новые интересные функции. Коммиттер портов захочет использовать версию в разработке, так как именно она используется в продакшене и содержит все новые функции, которые гарантируют, что всё будет работать идеально. Контрибьютору не обязательно нужны эти функции, так как наиболее важные исправления переносятся в выпущенную версию. Основная причина использования версии в разработке для сборки официальных пакетов заключается в её скорости — она позволяет сократить время полной сборки с 18 до 17 часов при использовании высокопроизводительного сервера с 32 CPU и 128 ГБ оперативной памяти. Эти оптимизации не будут столь значимы при сборке портов на настольном компьютере. [[testing-poudriere-setup]] === Настройка poudriere Порт устанавливает файл конфигурации по умолчанию, [.filename]#/usr/local/etc/poudriere.conf#. Каждый параметр описан в этом файле конфигурации. Вот минимальный пример конфигурационного файла: [.programlisting] .... ZPOOL=zroot BASEFS=/usr/local/poudriere DISTFILES_CACHE=/usr/ports/distfiles RESOLV_CONF=/etc/resolv.conf .... `ZPOOL`:: Имя пула хранения ZFS, который будет использовать poudriere. Должно быть указано в выводе команды `zpool status`. `BASEFS`:: Корневая точка монтирования файловых систем poudriere. Эта запись приведет к тому, что poudriere смонтирует `tank/poudriere` в `/poudriere`. `DISTFILES_CACHE`:: Определяет, где хранятся distfiles. В этом примере poudriere и хост используют общий каталог для хранения distfiles. Это позволяет избежать загрузки tарболов, которые уже присутствуют в системе. Пожалуйста, создайте этот каталог, если он ещё не существует, чтобы poudriere мог его найти. `RESOLV_CONF`:: Используйте файл [.filename]#/etc/resolv.conf# хоста внутри клеток для DNS. Это необходимо, чтобы клетки могли разрешать URL-адреса distfiles при загрузке. Это не требуется при использовании прокси. Обратитесь к файлу конфигурации по умолчанию для настройки прокси. [[testing-poudriere-create-jails]] === Создание клеток poudriere Создайте базовые клетки, которые poudriere будет использовать для сборки: [source, shell] .... # poudriere jail -c -j 143Ramd64 -v 14.3-RELEASE -a amd64 .... Загрузите `14.3-RELEASE` для `amd64` с HTTPS-сервера, указанного в `FREEBSD_HOST` в [.filename]#poudriere.conf#, создайте ZFS-файловую систему `tank/poudriere/jails/143Ramd64`, смонтируйте её в [.filename]#/poudriere/jails/143Ramd64# и распакуйте тарболлы `14.3-RELEASE` в эту файловую систему. [source, shell] .... # poudriere jail -c -j 13i386 -v stable/13 -a i386 -m git+https .... Создайте `tank/poudriere/jails/13i386`, смонтируйте его на [.filename]#/poudriere/jails/13i386#, затем извлеките верхушку ветки Git `FreeBSD-13-STABLE` из `GIT_HOST` в [.filename]#poudriere.conf# или по умолчанию `git.freebsd.org` в [.filename]#/poudriere/jails/13i386/usr/src#, после чего выполните `buildworld` и установите его в [.filename]#/poudriere/jails/13i386#. [NOTE] ==== Хотя возможно собрать более новую версию FreeBSD на старой версии, в большинстве случаев она не запустится. Например, если требуется клетка на `stable/14`, то хост также должен работать на `stable/14`. Запуск `14.3-RELEASE` недостаточен. ==== [NOTE] ==== Для создания клетки poudriere для `16.0-CURRENT`: [source, shell] .... # poudriere jail -c -j 16amd64 -v main -a amd64 -m git+https .... Для запуска клетки `16.0-CURRENT` poudriere хостовая система должна работать под управлением `16.0-CURRENT`. В общем случае, более новые ядра могут собирать и запускать более старые клетки. Например, ядро `16.0-CURRENT` может собирать и запускать клетку `14.3-STABLE`, если параметр ядра `COMPAT_FREEBSD14` был скомпилирован (включен по умолчанию в конфигурации ядра [.filename]#GENERIC# `16.0-CURRENT`). ==== Список клеток, известных poudriere, можно вывести с помощью команды `poudriere jail -l`: [source, shell] .... # poudriere jail -l JAILNAME VERSION ARCH METHOD 143Ramd64 14.3-RELEASE amd64 http 13i386 13.5-STABLE i386 git+https .... [[testing-poudriere-maintaining-jails]] === Обновление клеток poudriere Управление обновлениями очень простое. Команда: [source, shell] .... # poudriere jail -u -j JAILNAME .... обновляет указанную клетку до последней доступной версии. Для релизов FreeBSD обновление до последнего уровня исправлений с помощью man:freebsd-update[8]. Для версий FreeBSD, собранных из исходников, обновление до последней ревизии git в ветке. [TIP] ==== Для клеток, использующих метод `git+*`, полезно добавить `-J _КоличествоПараллельныхСборок_` для ускорения сборки за счёт увеличения количества параллельных задач компиляции. Например, если на машине для сборки 6 CPU, используйте: [source, shell] .... # poudriere jail -u -J 6 -j JAILNAME .... ==== [[testing-poudriere-ports-tree]] === Настройка деревьев портов для использования с poudriere Существует несколько способов использования деревьев портов в poudriere. Наиболее простой способ — позволить poudriere создать для себя дерево портов по умолчанию, используя link:{handbook}mirrors/#git[Git]: [source, shell] .... # poudriere ports -c -m git+https -B main .... Эти команды создают `tank/poudriere/ports/default`, монтируют его в [.filename]#/poudriere/ports/default# и заполняют с помощью Git. После этого он включается в список известных деревьев портов: [source, shell] .... # poudriere ports -l PORTSTREE METHOD TIMESTAMP PATH default git+https 2025-07-20 04:23:56 /poudriere/ports/default .... [NOTE] ==== Обратите внимание, что дерево портов "default" является особым. Каждая из команд сборки, объяснённых далее, будет неявно использовать это дерево портов, если явно не указано иное. Чтобы использовать другое дерево, добавьте `-p _treename_` к командам. ==== Лучший способ работы с локальными изменениями для разработчика портов — использовать link:{handbook}mirrors/#git[Git]. Как и при создании клеток, можно использовать другой метод для создания дерева портов. Чтобы добавить дополнительное дерево портов для тестирования локальных изменений и разработки портов, предпочтительно использовать клонирование дерева через git (как описано выше). [[testing-poudriere-ports-tree-manual]] === Использование управляемых вручную деревьев портов с помощью poudriere В зависимости от рабочего процесса может быть крайне полезно использовать деревья портов, которые поддерживаются вручную. Например, если существует локальная копия дерева портов в [.filename]#/work/ports#, укажите poudriere на это расположение: [source, shell] .... # poudriere ports -c -m null -M /work/ports -p development .... Это будет указано в таблице известных деревьев: [source, shell] .... # poudriere ports -l PORTSTREE METHOD TIMESTAMP PATH development null 2025-07-20 05:06:33 /work/ports .... [NOTE] ==== Тире или `null` в колонке `METHOD` означает, что poudriere никогда не будет обновлять или изменять это дерево портов. Полностью на пользователе лежит ответственность за поддержку этого дерева, включая все локальные изменения, которые могут использоваться для тестирования новых портов и отправки исправлений. ==== [[testing-poudriere-ports-tree-updating]] === Обновление деревьев портов poudriere Так же просто, как с клетками, описанными ранее: [source, shell] .... # poudriere ports -u -p PORTSTREE .... Обновит указанное _PORTSTREE_, дерево, указанное в выводе команды `poudriere -l`, до последней доступной ревизии на официальных серверах. [NOTE] ==== Деревья портов без метода, см. crossref:testing[testing-poudriere-ports-tree-manual, Использование вручную управляемых деревьев портов с помощью poudriere], не могут быть обновлены таким образом и должны обновляться вручную сопровождающим портов. ==== [[testing-poudriere-testing-ports]] === Тестирование портов После настройки клеток и деревьев портов можно проверить результат изменений, внесенных участником в дерево портов. Например, локальные изменения в порте package:www/firefox[], расположенном в [.filename]#/work/ports/www/firefox#, можно протестировать в ранее созданной клетке 14.3-RELEASE: [source, shell] .... # poudriere testport -j 143Ramd64 -p development -o www/firefox .... Это соберет все зависимости Firefox. Если зависимость уже была собрана ранее и остаётся актуальной, будет установлен готовый пакет. Если для зависимости нет актуального пакета, он будет собран с параметрами по умолчанию в клетке. Затем будет собран сам Firefox. Полная сборка каждого порта записывается в [.filename]#/poudriere/data/logs/bulk/143Ri386-development/build-time/logs#. Имя каталога `143Ri386-development` формируется из аргументов `-j` и `-p` соответственно. Для удобства также поддерживается символическая ссылка [.filename]#/poudriere/data/logs/bulk/143Ri386-development/latest#. Эта ссылка указывает на последний каталог _времени сборки_. Также в этом каталоге находится файл [.filename]#index.html#, который позволяет наблюдать за процессом сборки через веб-браузер. По умолчанию poudriere очищает клетки и оставляет файлы журналов в указанных выше каталогах. Для упрощения анализа клетки можно оставить запущенными после сборки, добавив `-i` к `testport`: [source, shell] .... # poudriere testport -j 143Ramd64 -p development -i -o www/firefox .... После завершения сборки, независимо от того, была ли она успешной, в клетке предоставляется оболочка. Эта оболочка используется для дальнейшего исследования. Можно указать poudriere оставить клетку запущенной после завершения сборки с помощью `-I`. poudriere покажет команду для выполнения, когда клетка больше не нужна. Затем можно использовать man:jexec[8] для входа в неё: [source, shell] .... # poudriere testport -j 143Ramd64 -p development -I -o www/firefox [...] ====>> Installing local Pkg repository to /usr/local/etc/pkg/repos ====>> Leaving jail 143Ramd64-development-n running, mounted at /poudriere/data/.m/143Ramd64-development/ref for interactive run testing ====>> To enter jail: jexec 143Ramd64-development-n env -i TERM=$TERM /usr/bin/login -fp root ====>> To stop jail: poudriere jail -k -j 143Ramd64 -p development # jexec 143Ramd64-development-n env -i TERM=$TERM /usr/bin/login -fp root # [do some stuff in the jail] # exit # poudriere jail -k -j 143Ramd64 -p development ====>> Umounting file systems .... Неотъемлемой частью инфраструктуры сборки портов FreeBSD является возможность настройки портов под личные предпочтения с помощью опций. Их также можно тестировать с помощью poudriere. Добавление опции `-c`: [source, shell] .... # poudriere testport -j 143Ramd64 -c -o www/firefox .... Представляет диалог настройки порта перед его сборкой. Порты, указанные после `-o` в формате `_категория_/_имя_порта_`, будут использовать указанные опции, все зависимости будут использовать опции по умолчанию. Тестирование зависимых портов с нестандартными опциями может быть выполнено с использованием наборов, см. crossref:testing[testing-poudriere-sets, Использование наборов]. [TIP] ==== При тестировании портов, где файл [.filename]#pkg-plist# изменяется во время сборки в зависимости от выбранных опций, рекомендуется выполнить тестовый запуск со всеми выбранными опциями _и_ один без выбранных опций. ==== [[testing-poudriere-sets]] === Использование наборов Для всех действий, связанных со сборкой, можно указать так называемый _набор_ с помощью `-z _имя_набора_`. Набор относится к полностью независимой сборке. Это позволяет, например, использовать `testport` с нестандартными параметрами для зависимых портов. Для использования наборов poudriere ожидает, что будет использована структура каталогов, аналогичная `PORT_DBDIR`, по умолчанию [.filename]#/var/db/ports#, в его конфигурационном каталоге. Этот каталог затем монтируется с помощью man:nullfs[5] в клетки, где собираются порты и их зависимости. Обычно подходящую начальную точку можно получить, рекурсивно скопировав существующий `PORT_DBDIR` в [.filename]#/usr/local/etc/poudriere.d/jailname-portname-setname-options#. Это подробно описано в man:poudriere[8]. Например, для тестирования package:www/firefox[] в определённом наборе с именем `devset`, добавьте параметр `-z devset` к команде `testport`: [source, shell] .... # poudriere testport -j 143Ramd64 -p development -z devset -o www/firefox .... Это проверит наличие этих каталогов в следующем порядке: * [.filename]#/usr/local/etc/poudriere.d/143Ramd64-development-devset-options# * [.filename]#/usr/local/etc/poudriere.d/143Ramd64-devset-options# * [.filename]#/usr/local/etc/poudriere.d/143Ramd64-development-options# * [.filename]#/usr/local/etc/poudriere.d/devset-options# * [.filename]#/usr/local/etc/poudriere.d/development-options# * [.filename]#/usr/local/etc/poudriere.d/143Ramd64-options# * [.filename]#/usr/local/etc/poudriere.d/options# Из этого списка poudriere man:nullfs[5] монтирует _первое существующее_ дерево каталогов в каталог [.filename]#/var/db/ports# сборных клеток. Таким образом, все пользовательские настройки используются для всех портов во время этого запуска `testport`. После предоставления структуры каталогов для набора можно изменить параметры для конкретного порта. Например: [source, shell] .... # poudriere options -c www/firefox -z devset .... Отображается диалог настройки package:www/firefox[], где можно редактировать параметры. Выбранные параметры сохраняются в набор `devset`. [NOTE] ==== poudriere очень гибок в настройке опций. poudriere можно настроить для конкретных клеток, деревьев портов и для нескольких портов одной командой. Подробности см. в man:poudriere[8]. ==== [[testing-poudriere-make-conf]] === Предоставление пользовательского файла [.filename]#make.conf# Подобно использованию наборов, poudriere также использует пользовательский [.filename]#make.conf#, если он предоставлен. Для этого не требуется специального аргумента командной строки. Вместо этого poudriere ищет существующие файлы, соответствующие схеме именования, производной от командной строки. Например: [source, shell] .... # poudriere testport -j 143Ramd64 -p development -z devset -o www/firefox .... заставляет poudriere проверять наличие этих файлов в следующем порядке: * [.filename]#/usr/local/etc/poudriere.d/make.conf# * [.filename]#/usr/local/etc/poudriere.d/devset-make.conf# * [.filename]#/usr/local/etc/poudriere.d/development-make.conf# * [.filename]#/usr/local/etc/poudriere.d/143Ramd64-make.conf# * [.filename]#/usr/local/etc/poudriere.d/143Ramd64-development-make.conf# * [.filename]#/usr/local/etc/poudriere.d/143Ramd64-devset-make.conf# * [.filename]#/usr/local/etc/poudriere.d/143Ramd64-development-devset-make.conf# В отличие от наборов, все найденные файлы будут добавлены, _в указанном порядке_, в один [.filename]#make.conf# внутри клеток сборки. Таким образом, можно задать общие переменные make, предназначенные для влияния на все сборки, в файле [.filename]#/usr/local/etc/poudriere.d/make.conf#. Специальные переменные, предназначенные только для определённых клеток или наборов, можно задать в специализированных файлах [.filename]#make.conf#, например, в [.filename]#/usr/local/etc/poudriere.d/143Ramd64-development-devset-make.conf#. [[testing-poudriere-sets-perl]] .Использование [.filename]#make.conf# для изменения Perl по умолчанию [example] ==== Для сборки набора с нестандартной версией Perl, например, `5.20`, используя набор с именем `perl5-20`, создайте файл [.filename]#perl5-20-make.conf# со следующей строкой: [.programlisting] .... DEFAULT_VERSIONS+= perl=5.20 .... [NOTE] -**** +===== Обратите внимание на использование `+=`, чтобы содержимое переменной не было перезаписано, если она уже установлена в стандартном [.filename]#make.conf#. -**** +===== ==== [[testing-poudriere-pruning-distfiles]] === Удаление ненужных файлов дистрибутива poudriere имеет встроенный механизм для удаления устаревших файлов дистрибутива, которые больше не используются ни одним портом данного дерева. Команда [source, shell] .... # poudriere distclean -p portstree .... будет сканировать папку файлов дистрибутива, `DISTFILES_CACHE` в [.filename]#poudriere.conf#, сравнивая её с деревом портов, указанным аргументом `-p _portstree_`, и запрашивать подтверждение на удаление этих файлов дистрибутива. Чтобы пропустить запрос и удалить все неиспользуемые файлы без подтверждения, можно добавить аргумент `-y`: [source, shell] .... # poudriere distclean -p portstree -y .... [[testing-debugging-ports]] == Отладка портов Иногда что-то идёт не так, и порт не работает во время выполнения. Фреймворк предоставляет некоторые средства для отладки портов. Эти вспомогательные инструменты ограничены, поскольку способ отладки порта во многом зависит от используемой технологии. Следующие переменные помогают в отладке портов: * `WITH_DEBUG`. Если установлено, порты собираются с отладочными символами. * `WITH_DEBUG_PORTS`. Указывает список портов, которые должны собираться с установленным `WITH_DEBUG`. * `DEBUG_FLAGS`. Используется для указания дополнительных флагов для `CFLAGS`. По умолчанию `-g`. Когда `WITH_DEBUG` установлен, глобально или для списка портов, результирующие бинарные файлы не лишаются символов. Эти переменные могут быть указаны в [.filename]#make.conf# или в командной строке: [source, shell] .... # cd category/port && make -DWITH_DEBUG DEBUG_FLAGSS="-g -O0" .... [NOTE] ==== Если порт собирается с использованием package:ports-mgmt/poudriere[], отладочные переменные должны быть указаны в [.filename]#make.conf# poudriere, а не в [.filename]#/etc/make.conf#. Подробности см. в документации package:ports-mgmt/poudriere[]. ==== Пожалуйста, обратитесь к отладочной информации в extref:{developers-handbook}tools[Руководстве разработчика, отладка] для получения более подробной информации о доступных инструментах отладки. diff --git a/documentation/content/zh-tw/books/developers-handbook/secure/_index.adoc b/documentation/content/zh-tw/books/developers-handbook/secure/_index.adoc index 3856161a18..ffd9c2af89 100644 --- a/documentation/content/zh-tw/books/developers-handbook/secure/_index.adoc +++ b/documentation/content/zh-tw/books/developers-handbook/secure/_index.adoc @@ -1,226 +1,226 @@ --- title: 章 3. Secure Programming -authors: +authors: - author: Murray Stokely prev: books/developers-handbook/tools next: books/developers-handbook/l10n showBookMenu: true weight: 4 params: path: "/books/developers-handbook/secure/" --- [[secure]] = Secure Programming :doctype: book :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumoffset: 3 :partnums: :source-highlighter: rouge :experimental: :images-path: books/developers-handbook/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] [[secure-synopsis]] == Synopsis This chapter describes some of the security issues that have plagued UNIX(R) programmers for decades and some of the new tools available to help programmers avoid writing exploitable code. [[secure-philosophy]] == Secure Design Methodology Writing secure applications takes a very scrutinous and pessimistic outlook on life. Applications should be run with the principle of "least privilege" so that no process is ever running with more than the bare minimum access that it needs to accomplish its function. Previously tested code should be reused whenever possible to avoid common mistakes that others may have already fixed. One of the pitfalls of the UNIX(R) environment is how easy it is to make assumptions about the sanity of the environment. Applications should never trust user input (in all its forms), system resources, inter-process communication, or the timing of events. UNIX(R) processes do not execute synchronously so logical operations are rarely atomic. [[secure-bufferov]] == Buffer Overflows Buffer Overflows have been around since the very beginnings of the von Neumann crossref:bibliography[COD,1] architecture. They first gained widespread notoriety in 1988 with the Morris Internet worm. Unfortunately, the same basic attack remains effective today. By far the most common type of buffer overflow attack is based on corrupting the stack. Most modern computer systems use a stack to pass arguments to procedures and to store local variables. A stack is a last in first out (LIFO) buffer in the high memory area of a process image. When a program invokes a function a new "stack frame" is created. This stack frame consists of the arguments passed to the function as well as a dynamic amount of local variable space. The "stack pointer" is a register that holds the current location of the top of the stack. Since this value is constantly changing as new values are pushed onto the top of the stack, many implementations also provide a "frame pointer" that is located near the beginning of a stack frame so that local variables can more easily be addressed relative to this value. crossref:bibliography[COD,1] The return address for function calls is also stored on the stack, and this is the cause of stack-overflow exploits since overflowing a local variable in a function can overwrite the return address of that function, potentially allowing a malicious user to execute any code he or she wants. Although stack-based attacks are by far the most common, it would also be possible to overrun the stack with a heap-based (malloc/free) attack. The C programming language does not perform automatic bounds checking on arrays or pointers as many other languages do. In addition, the standard C library is filled with a handful of very dangerous functions. [.informaltable] [cols="1,1", frame="none"] |=== |`strcpy`(char *dest, const char *src) | May overflow the dest buffer |`strcat`(char *dest, const char *src) | May overflow the dest buffer |`getwd`(char *buf) | May overflow the buf buffer |`gets`(char *s) | May overflow the s buffer |`[vf]scanf`(const char *format, ...) | May overflow its arguments. |`realpath`(char *path, char resolved_path[]) | May overflow the path buffer |`[v]sprintf`(char *str, const char *format, ...) | May overflow the str buffer. |=== === Example Buffer Overflow The following example code contains a buffer overflow designed to overwrite the return address and skip the instruction immediately following the function call. (Inspired by crossref:bibliography[Phrack,4]) [.programlisting] .... #include void manipulate(char *buffer) { char newbuffer[80]; strcpy(newbuffer,buffer); } int main() { char ch,buffer[4096]; int i=0; while ((buffer[i++] = getchar()) != '\n') {}; i=1; manipulate(buffer); i=2; printf("The value of i is : %d\n",i); return 0; } .... Let us examine what the memory image of this process would look like if we were to input 160 spaces into our little program before hitting return. -[XXX figure here!] +[ XXX figure here! ] Obviously more malicious input can be devised to execute actual compiled instructions (such as exec(/bin/sh)). === Avoiding Buffer Overflows The most straightforward solution to the problem of stack-overflows is to always use length restricted memory and string copy functions. `strncpy` and `strncat` are part of the standard C library. These functions accept a length value as a parameter which should be no larger than the size of the destination buffer. These functions will then copy up to 'length' bytes from the source to the destination. However there are a number of problems with these functions. Neither function guarantees NUL termination if the size of the input buffer is as large as the destination. The length parameter is also used inconsistently between strncpy and strncat so it is easy for programmers to get confused as to their proper usage. There is also a significant performance loss compared to `strcpy` when copying a short string into a large buffer since `strncpy` NUL fills up the size specified. Another memory copy implementation exists to get around these problems. The `strlcpy` and `strlcat` functions guarantee that they will always null terminate the destination string when given a non-zero length argument. ==== Compiler based run-time bounds checking Unfortunately there is still a very large assortment of code in public use which blindly copies memory around without using any of the bounded copy routines we just discussed. Fortunately, there is a way to help prevent such attacks - run-time bounds checking, which is implemented by several C/C++ compilers. ProPolice is one such compiler feature, and is integrated into man:gcc[1] versions 4.1 and later. It replaces and extends the earlier StackGuard man:gcc[1] extension. ProPolice helps to protect against stack-based buffer overflows and other attacks by laying pseudo-random numbers in key areas of the stack before calling any function. When a function returns, these "canaries" are checked and if they are found to have been changed the executable is immediately aborted. Thus any attempt to modify the return address or other variable stored on the stack in an attempt to get malicious code to run is unlikely to succeed, as the attacker would have to also manage to leave the pseudo-random canaries untouched. Recompiling your application with ProPolice is an effective means of stopping most buffer-overflow attacks, but it can still be compromised. ==== Library based run-time bounds checking Compiler-based mechanisms are completely useless for binary-only software for which you cannot recompile. For these situations there are a number of libraries which re-implement the unsafe functions of the C-library (`strcpy`, `fscanf`, `getwd`, etc..) and ensure that these functions can never write past the stack pointer. * libsafe * libverify * libparanoia Unfortunately these library-based defenses have a number of shortcomings. These libraries only protect against a very small set of security related issues and they neglect to fix the actual problem. These defenses may fail if the application was compiled with -fomit-frame-pointer. Also, the LD_PRELOAD and LD_LIBRARY_PATH environment variables can be overwritten/unset by the user. [[secure-setuid]] == SetUID issues There are at least 6 different IDs associated with any given process. Because of this you have to be very careful with the access that your process has at any given time. In particular, all seteuid applications should give up their privileges as soon as it is no longer required. The real user ID can only be changed by a superuser process. The login program sets this when a user initially logs in and it is seldom changed. The effective user ID is set by the `exec()` functions if a program has its seteuid bit set. An application can call `seteuid()` at any time to set the effective user ID to either the real user ID or the saved set-user-ID. When the effective user ID is set by `exec()` functions, the previous value is saved in the saved set-user-ID. [[secure-chroot]] == Limiting your program's environment The traditional method of restricting a process is with the `chroot()` system call. This system call changes the root directory from which all other paths are referenced for a process and any child processes. For this call to succeed the process must have execute (search) permission on the directory being referenced. The new environment does not actually take effect until you `chdir()` into your new environment. It should also be noted that a process can easily break out of a chroot environment if it has root privilege. This could be accomplished by creating device nodes to read kernel memory, attaching a debugger to a process outside of the man:chroot[8] environment, or in many other creative ways. The behavior of the `chroot()` system call can be controlled somewhat with the kern.chroot_allow_open_directories `sysctl` variable. When this value is set to 0, `chroot()` will fail with EPERM if there are any directories open. If set to the default value of 1, then `chroot()` will fail with EPERM if there are any directories open and the process is already subject to a `chroot()` call. For any other value, the check for open directories will be bypassed completely. === FreeBSD's jail functionality The concept of a Jail extends upon the `chroot()` by limiting the powers of the superuser to create a true `virtual server'. Once a prison is set up all network communication must take place through the specified IP address, and the power of "root privilege" in this jail is severely constrained. While in a prison, any tests of superuser power within the kernel using the `suser()` call will fail. However, some calls to `suser()` have been changed to a new interface `suser_xxx()`. This function is responsible for recognizing or denying access to superuser power for imprisoned processes. A superuser process within a jailed environment has the power to: * Manipulate credential with `setuid`, `seteuid`, `setgid`, `setegid`, `setgroups`, `setreuid`, `setregid`, `setlogin` * Set resource limits with `setrlimit` * Modify some sysctl nodes (kern.hostname) * `chroot()` * Set flags on a vnode: `chflags`, `fchflags` * Set attributes of a vnode such as file permission, owner, group, size, access time, and modification time. * Bind to privileged ports in the Internet domain (ports < 1024) `Jail` is a very useful tool for running applications in a secure environment but it does have some shortcomings. Currently, the IPC mechanisms have not been converted to the `suser_xxx` so applications such as MySQL cannot be run within a jail. Superuser access may have a very limited meaning within a jail, but there is no way to specify exactly what "very limited" means. === POSIX(R).1e Process Capabilities POSIX(R) has released a working draft that adds event auditing, access control lists, fine grained privileges, information labeling, and mandatory access control. This is a work in progress and is the focus of the http://www.trustedbsd.org/[TrustedBSD] project. Some of the initial work has been committed to FreeBSD-CURRENT (cap_set_proc(3)). [[secure-trust]] == Trust An application should never assume that anything about the users environment is sane. This includes (but is certainly not limited to): user input, signals, environment variables, resources, IPC, mmaps, the filesystem working directory, file descriptors, the # of open files, etc. You should never assume that you can catch all forms of invalid input that a user might supply. Instead, your application should use positive filtering to only allow a specific subset of inputs that you deem safe. Improper data validation has been the cause of many exploits, especially with CGI scripts on the world wide web. For filenames you need to be extra careful about paths ("../", "/"), symbolic links, and shell escape characters. Perl has a really cool feature called "Taint" mode which can be used to prevent scripts from using data derived outside the program in an unsafe way. This mode will check command line arguments, environment variables, locale information, the results of certain syscalls (`readdir()`, `readlink()`, `getpwxxx()`), and all file input. [[secure-race-conditions]] == Race Conditions A race condition is anomalous behavior caused by the unexpected dependence on the relative timing of events. In other words, a programmer incorrectly assumed that a particular event would always happen before another. Some of the common causes of race conditions are signals, access checks, and file opens. Signals are asynchronous events by nature so special care must be taken in dealing with them. Checking access with `access(2)` then `open(2)` is clearly non-atomic. Users can move files in between the two calls. Instead, privileged applications should `seteuid()` and then call `open()` directly. Along the same lines, an application should always set a proper umask before `open()` to obviate the need for spurious `chmod()` calls. diff --git a/documentation/content/zh-tw/books/porters-handbook/makefiles/_index.adoc b/documentation/content/zh-tw/books/porters-handbook/makefiles/_index.adoc index daf1b6101a..a61e5395b2 100644 --- a/documentation/content/zh-tw/books/porters-handbook/makefiles/_index.adoc +++ b/documentation/content/zh-tw/books/porters-handbook/makefiles/_index.adoc @@ -1,5321 +1,5321 @@ --- title: Chapter 5. Configuring the Makefile prev: books/porters-handbook/slow-porting next: books/porters-handbook/special description: Configuring the Makefile for FreeBSD Ports tags: ["makefiles", "configuring", "naming", "versions"] showBookMenu: true weight: 5 params: path: "/books/porters-handbook/makefiles/" --- [[makefiles]] = Configuring the Makefile :doctype: book :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumoffset: 5 :partnums: :source-highlighter: rouge :experimental: :g-plus-plus: g++ :images-path: books/porters-handbook/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] Configuring the [.filename]#Makefile# is pretty simple, and again we suggest looking at existing examples before starting. Also, there is a crossref:porting-samplem[porting-samplem,sample Makefile] in this handbook, so take a look and please follow the ordering of variables and sections in that template to make the port easier for others to read. Consider these problems in sequence during the design of the new [.filename]#Makefile#: [[makefile-source]] == The Original Source Does it live in `DISTDIR` as a standard ``gzip``ped tarball named something like [.filename]#foozolix-1.2.tar.gz#? If so, go on to the next step. If not, the distribution file format might require overriding one or more of `DISTVERSION`, `DISTNAME`, `EXTRACT_CMD`, `EXTRACT_BEFORE_ARGS`, `EXTRACT_AFTER_ARGS`, `EXTRACT_SUFX`, or `DISTFILES`. In the worst case, create a custom `do-extract` target to override the default. This is rarely, if ever, necessary. [[makefile-naming]] == Naming The first part of the port's [.filename]#Makefile# names the port, describes its version number, and lists it in the correct category. [[makefile-portname]] === `PORTNAME` Set `PORTNAME` to the base name of the software. It is used as the base for the FreeBSD package, and for <>. [IMPORTANT] ==== The package name must be unique across the entire ports tree. Make sure that the `PORTNAME` is not already in use by an existing port, and that no other port already has the same `PKGBASE`. If the name has already been used, add either <>. ==== [[makefile-versions]] === Versions, `DISTVERSION` _or_ `PORTVERSION` Set `DISTVERSION` to the version number of the software. `PORTVERSION` is the version used for the FreeBSD package. It will be automatically derived from `DISTVERSION` to be compatible with FreeBSD's package versioning scheme. If the version contains _letters_, it might be needed to set `PORTVERSION` and not `DISTVERSION`. [IMPORTANT] ==== Only one of `PORTVERSION` and `DISTVERSION` can be set at a time. ==== From time to time, some software will use a version scheme that is not compatible with how `DISTVERSION` translates in `PORTVERSION`. [TIP] ==== When updating a port, it is possible to use man:pkg-version[8]'s `-t` argument to check if the new version is greater or lesser than before. See <>. ==== [[makefile-versions-ex-pkg-version]] .Using man:pkg-version[8] to Compare Versions [example] ==== `pkg version -t` takes two versions as arguments, it will respond with `<`, `=` or `>` if the first version is less, equal, or more than the second version, respectively. [source,shell] .... % pkg version -t 1.2 1.3 < <.> % pkg version -t 1.2 1.2 = <.> % pkg version -t 1.2 1.2.0 = <.> % pkg version -t 1.2 1.2.p1 > <.> % pkg version -t 1.2.a1 1.2.b1 < <.> % pkg version -t 1.2 1.2p1 < <.> .... <.> `1.2` is before `1.3`. <.> `1.2` and `1.2` are equal as they have the same version. <.> `1.2` and `1.2.0` are equal as nothing equals zero. <.> `1.2` is after `1.2.p1` as `.p1`, think "pre-release 1". <.> `1.2.a1` is before `1.2.b1`, think "alpha" and "beta", and `a` is before `b`. <.> `1.2` is before `1.2p1` as `2p1`, think "2, patch level 1" which is a version after any `2.X` but before `3`. [NOTE] -**** +===== In here, the `a`, `b`, and `p` are used as if meaning "alpha", "beta" or "pre-release" and "patch level", but they are only letters and are sorted alphabetically, so any letter can be used, and they will be sorted appropriately. -**** +===== ==== .Examples of `DISTVERSION` and the Derived `PORTVERSION` [cols="10%,90%", frame="none", options="header"] |=== | DISTVERSION | PORTVERSION |0.7.1d |0.7.1.d |10Alpha3 |10.a3 |3Beta7-pre2 |3.b7.p2 |8:f_17 |8f.17 |=== [[makefile-versions-ex1]] .Using `DISTVERSION` [example] ==== When the version only contains numbers separated by dots, dashes or underscores, use `DISTVERSION`. [.programlisting] .... PORTNAME= nekoto DISTVERSION= 1.2-4 .... It will generate a `PORTVERSION` of `1.2.4`. ==== [[makefile-versions-ex2]] .Using `DISTVERSION` When the Version Starts with a Letter or a Prefix [example] ==== When the version starts or ends with a letter, or a prefix or a suffix that is not part of the version, use `DISTVERSIONPREFIX`, `DISTVERSION`, and `DISTVERSIONSUFFIX`. If the version is `v1.2-4`: [.programlisting] .... PORTNAME= nekoto DISTVERSIONPREFIX= v DISTVERSION= 1_2_4 .... Some of the time, projects using GitHub will use their name in their versions. For example, the version could be `nekoto-1.2-4`: [.programlisting] .... PORTNAME= nekoto DISTVERSIONPREFIX= nekoto- DISTVERSION= 1.2_4 .... Those projects also sometimes use some string at the end of the version, for example, `1.2-4_RELEASE`: [.programlisting] .... PORTNAME= nekoto DISTVERSION= 1.2-4 DISTVERSIONSUFFIX= _RELEASE .... Or they do both, for example, `nekoto-1.2-4_RELEASE`: [.programlisting] .... PORTNAME= nekoto DISTVERSIONPREFIX= nekoto- DISTVERSION= 1.2-4 DISTVERSIONSUFFIX= _RELEASE .... `DISTVERSIONPREFIX` and `DISTVERSIONSUFFIX` will not be used while constructing `PORTVERSION`, but only used in `DISTNAME`. All will generate a `PORTVERSION` of `1.2.4`. ==== [[makefile-versions-ex3]] .Using `DISTVERSION` When the Version Contains Letters Meaning "alpha", "beta", or "pre-release" [example] ==== When the version contains numbers separated by dots, dashes or underscores, and letters are used to mean "alpha", "beta" or "pre-release", which is, before the version without the letters, use `DISTVERSION`. [.programlisting] .... PORTNAME= nekoto DISTVERSION= 1.2-pre4 .... [.programlisting] .... PORTNAME= nekoto DISTVERSION= 1.2p4 .... Both will generate a `PORTVERSION` of `1.2.p4` which is before than 1.2. man:pkg-version[8] can be used to check that fact: [source,shell] .... % pkg version -t 1.2.p4 1.2 < .... ==== [[makefile-versions-ex4]] .Not Using `DISTVERSION` When the Version Contains Letters Meaning "Patch Level" [example] ==== When the version contains letters that are not meant as "alpha", "beta", or "pre", but more in a "patch level", and meaning after the version without the letters, use `PORTVERSION`. [.programlisting] .... PORTNAME= nekoto PORTVERSION= 1.2p4 .... In this case, using `DISTVERSION` is not possible because it would generate a version of `1.2.p4` which would be before `1.2` and not after. man:pkg-version[8] will verify this: [source,shell] .... % pkg version -t 1.2 1.2.p4 > <.> % pkg version -t 1.2 1.2p4 < <.> .... <.> `1.2` is after `1.2.p4`, which is _wrong_ in this case. <.> `1.2` is before `1.2p4`, which is what was needed. ==== For some more advanced examples of setting `PORTVERSION`, when the software's versioning is really not compatible with FreeBSD's, or `DISTNAME` when the distribution file does not contain the version itself, see <>. [[makefile-naming-revepoch]] === `PORTREVISION` and `PORTEPOCH` [[makefile-portrevision]] ==== `PORTREVISION` `PORTREVISION` is a monotonically increasing value which is reset to 0 with every increase of `DISTVERSION`, typically every time there is a new official vendor release. If `PORTREVISION` is non-zero, the value is appended to the package name. Changes to `PORTREVISION` are used by automated tools like man:pkg-version[8] to determine that a new package is available. `PORTREVISION` must be increased each time a change is made to the port that changes the generated package in any way. That includes changes that only affect a package built with non-default <>. Examples of when `PORTREVISION` must be bumped: * Addition of patches to correct security vulnerabilities, bugs, or to add new functionality to the port. * Changes to the port [.filename]#Makefile# to enable or disable compile-time options in the package. * Changes in the packing list or the install-time behavior of the package. For example, a change to a script which generates initial data for the package, like man:ssh[1] host keys. * Version bump of a port's shared library dependency (in this case, someone trying to install the old package after installing a newer version of the dependency will fail since it will look for the old libfoo.x instead of libfoo.(x+1)). * Silent changes to the port distfile which have significant functional differences. For example, changes to the distfile requiring a correction to [.filename]#distinfo# with no corresponding change to `DISTVERSION`, where a `diff -ru` of the old and new versions shows non-trivial changes to the code. Examples of changes which do not require a `PORTREVISION` bump: * Style changes to the port skeleton with no functional change to what appears in the resulting package. * Changes to `MASTER_SITES` or other functional changes to the port which do not affect the resulting package. * Trivial patches to the distfile such as correction of typos, which are not important enough that users of the package have to go to the trouble of upgrading. * Build fixes which cause a package to become compilable where it was previously failing. As long as the changes do not introduce any functional change on any other platforms on which the port did previously build. Since `PORTREVISION` reflects the content of the package, if the package was not previously buildable then there is no need to increase `PORTREVISION` to mark a change. A rule of thumb is to decide whether a change committed to a port is something which _some_ people would benefit from having. Either because of an enhancement, fix, or by virtue that the new package will actually work at all. Then weigh that against that fact that it will cause everyone who regularly updates their ports tree to be compelled to update. If yes, `PORTREVISION` must be bumped. [NOTE] ==== People using binary packages will _never_ see the update if `PORTREVISION` is not bumped. Without increasing `PORTREVISION`, the package builders have no way to detect the change and thus, will not rebuild the package. ==== [[makefile-portepoch]] ==== `PORTEPOCH` From time to time a software vendor or FreeBSD porter will do something silly and release a version of their software which is actually numerically less than the previous version. An example of this is a port which goes from foo-20000801 to foo-1.0 (the former will be incorrectly treated as a newer version since 20000801 is a numerically greater value than 1). [TIP] ==== The results of version number comparisons are not always obvious. `pkg version` (see man:pkg-version[8]) can be used to test the comparison of two version number strings. For example: [source,shell] .... % pkg version -t 0.031 0.29 > .... The `>` output indicates that version 0.031 is considered greater than version 0.29, which may not have been obvious to the porter. ==== In situations such as this, `PORTEPOCH` must be increased. If `PORTEPOCH` is nonzero it is appended to the package name as described in section 0 above. `PORTEPOCH` must never be decreased or reset to zero, because that would cause comparison to a package from an earlier epoch to fail. For example, the package would not be detected as out of date. The new version number, `1.0,1` in the above example, is still numerically less than the previous version, 20000801, but the `,1` suffix is treated specially by automated tools and found to be greater than the implied suffix `,0` on the earlier package. Dropping or resetting `PORTEPOCH` incorrectly leads to no end of grief. If the discussion above was not clear enough, please consult the {freebsd-ports}. It is expected that `PORTEPOCH` will not be used for the majority of ports, and that sensible use of `DISTVERSION`, or that use `PORTVERSION` carefully, can often preempt it becoming necessary if a future release of the software changes the version structure. However, care is needed by FreeBSD porters when a vendor release is made without an official version number - such as a code "snapshot" release. The temptation is to label the release with the release date, which will cause problems as in the example above when a new "official" release is made. For example, if a snapshot release is made on the date `20000917`, and the previous version of the software was version `1.2`, do not use `20000917` for `DISTVERSION`. The correct way is a `DISTVERSION` of `1.2.20000917`, or similar, so that the succeeding release, say `1.3`, is still a numerically greater value. [[makefile-portrevision-example]] ==== Example of `PORTREVISION` and `PORTEPOCH` Usage The `gtkmumble` port, version `0.10`, is committed to the ports collection: [.programlisting] .... PORTNAME= gtkmumble DISTVERSION= 0.10 .... `PKGNAME` becomes `gtkmumble-0.10`. A security hole is discovered which requires a local FreeBSD patch. `PORTREVISION` is bumped accordingly. [.programlisting] .... PORTNAME= gtkmumble DISTVERSION= 0.10 PORTREVISION= 1 .... `PKGNAME` becomes `gtkmumble-0.10_1` A new version is released by the vendor, numbered `0.2` (it turns out the author actually intended `0.10` to actually mean `0.1.0`, not "what comes after 0.9" - oops, too late now). Since the new minor version `2` is numerically less than the previous version `10`, `PORTEPOCH` must be bumped to manually force the new package to be detected as "newer". Since it is a new vendor release of the code, `PORTREVISION` is reset to 0 (or removed from the [.filename]#Makefile#). [.programlisting] .... PORTNAME= gtkmumble DISTVERSION= 0.2 PORTEPOCH= 1 .... `PKGNAME` becomes `gtkmumble-0.2,1` The next release is 0.3. Since `PORTEPOCH` never decreases, the version variables are now: [.programlisting] .... PORTNAME= gtkmumble DISTVERSION= 0.3 PORTEPOCH= 1 .... `PKGNAME` becomes `gtkmumble-0.3,1` [NOTE] ==== If `PORTEPOCH` were reset to `0` with this upgrade, someone who had installed the `gtkmumble-0.10_1` package would not detect the `gtkmumble-0.3` package as newer, since `3` is still numerically less than `10`. Remember, this is the whole point of `PORTEPOCH` in the first place. ==== [[porting-pkgnameprefix-suffix]] === `PKGNAMEPREFIX` and `PKGNAMESUFFIX` Two optional variables, `PKGNAMEPREFIX` and `PKGNAMESUFFIX`, are combined with `PORTNAME` and `PORTVERSION` to form `PKGNAME` as `${PKGNAMEPREFIX}${PORTNAME}${PKGNAMESUFFIX}-${PORTVERSION}`. Make sure this conforms to our <>. In particular, the use of a hyphen (`-`) in `PORTVERSION` is _not_ allowed. Also, if the package name has the _language-_ or the _-compiled.specifics_ part (see below), use `PKGNAMEPREFIX` and `PKGNAMESUFFIX`, respectively. Do not make them part of `PORTNAME`. [[porting-pkgname]] === Package Naming Conventions These are the conventions to follow when naming packages. This is to make the package directory easy to scan, as there are already thousands of packages and users are going to turn away if they hurt their eyes! Package names take the form of [.filename]#language_region-name-compiled.specifics-version.numbers#. The package name is defined as `${PKGNAMEPREFIX}${PORTNAME}${PKGNAMESUFFIX}-${PORTVERSION}`. Make sure to set the variables to conform to that format. [[porting-pkgname-language]] [.filename]#language_region-#:: FreeBSD strives to support the native language of its users. The _language-_ part is a two letter abbreviation of the natural language defined by ISO-639 when the port is specific to a certain language. Examples are `ja` for Japanese, `ru` for Russian, `vi` for Vietnamese, `zh` for Chinese, `ko` for Korean and `de` for German. + If the port is specific to a certain region within the language area, add the two letter country code as well. Examples are `en_US` for US English and `fr_CH` for Swiss French. + The _language-_ part is set in `PKGNAMEPREFIX`. [[porting-pkgname-name]] [.filename]#name#:: Make sure that the port's name and version are clearly separated and placed into `PORTNAME` and `DISTVERSION`. The only reason for `PORTNAME` to contain a version part is if the upstream distribution is really named that way, as in the package:textproc/libxml2[] or package:japanese/kinput2-freewnn[] ports. Otherwise, `PORTNAME` cannot contain any version-specific information. It is quite normal for several ports to have the same `PORTNAME`, as the package:www/apache*[] ports do; in that case, different versions (and different index entries) are distinguished by `PKGNAMEPREFIX` and `PKGNAMESUFFIX` values. + There is a tradition of naming `Perl 5` modules by prepending `p5-` and converting the double-colon separator to a hyphen. For example, the `Data::Dumper` module becomes `p5-Data-Dumper`. [[porting-pkgname-compiled-specifics]] [.filename]#-compiled.specifics#:: If the port can be built with different <> (usually part of the directory name in a family of ports), the _-compiled.specifics_ part states the compiled-in defaults. The hyphen is optional. Examples are paper size and font units. + The _-compiled.specifics_ part is set in `PKGNAMESUFFIX`. [[porting-pkgname-version-numbers]] [.filename]#-version.numbers#:: The version string follows a dash (`-`) and is a period-separated list of integers and single lowercase alphabetics. In particular, it is not permissible to have another dash inside the version string. The only exception is the string `pl` (meaning "patchlevel"), which can be used _only_ when there are no major and minor version numbers in the software. If the software version has strings like "alpha", "beta", "rc", or "pre", take the first letter and put it immediately after a period. If the version string continues after those names, the numbers follow the single alphabet without an extra period between them (for example, `1.0b2`). + The idea is to make it easier to sort ports by looking at the version string. In particular, make sure version number components are always delimited by a period, and if the date is part of the string, use the `d__yyyy.mm.dd__` format, not `_dd.mm.yyyy_` or the non-Y2K compliant `_yy.mm.dd_` format. It is important to prefix the version with a letter, here `d` (for date), in case a release with an actual version number is made, which would be numerically less than `_yyyy_`. [IMPORTANT] ==== Package name must be unique among all of the ports tree, check that there is not already a port with the same `PORTNAME` and if there is add one of <>. ==== Here are some (real) examples on how to convert the name as called by the software authors to a suitable package name, for each line, only one of `DISTVERSION` or `PORTVERSION` is set in, depending on which would be used in the port's [.filename]#Makefile#: .Package Naming Examples [cols="1,1,1,1,1,1,1", frame="none", options="header"] |=== | Distribution Name | PKGNAMEPREFIX | PORTNAME | PKGNAMESUFFIX | DISTVERSION | PORTVERSION | Reason or comment |mule-2.2.2 |(empty) |mule |(empty) |2.2.2 | |No changes required |mule-1.0.1 |(empty) |mule |1 |1.0.1 | |This is version 1 of mule, and version 2 already exists |EmiClock-1.0.2 |(empty) |emiclock |(empty) |1.0.2 | |No uppercase names for single programs |rdist-1.3alpha |(empty) |rdist |(empty) |1.3alpha | |Version will be `1.3.a` |es-0.9-beta1 |(empty) |es |(empty) |0.9-beta1 | |Version will be `0.9.b1` |mailman-2.0rc3 |(empty) |mailman |(empty) |2.0rc3 | |Version will be `2.0.r3` |v3.3beta021.src |(empty) |tiff |(empty) | |3.3 |What the heck was that anyway? |tvtwm |(empty) |tvtwm |(empty) | |p11 |No version in the filename, use what upstream says it is |piewm |(empty) |piewm |(empty) |1.0 | |No version in the filename, use what upstream says it is |xvgr-2.10pl1 |(empty) |xvgr |(empty) | |2.10.pl1 |In that case, `pl1` means patch level, so using DISTVERSION is not possible. |gawk-2.15.6 |ja- |gawk |(empty) |2.15.6 | |Japanese language version |psutils-1.13 |(empty) |psutils |-letter |1.13 | |Paper size hardcoded at package build time |pkfonts |(empty) |pkfonts |300 |1.0 | |Package for 300dpi fonts |=== If there is absolutely no trace of version information in the original source and it is unlikely that the original author will ever release another version, just set the version string to `1.0` (like the `piewm` example above). Otherwise, ask the original author or use the date string the source file was released on (`d__yyyy.mm.dd__`, or `d__yyyymmdd__`) as the version. [TIP] ==== Use any letter. Here, `d` here stands for date, if the source is a Git repository, `g` followed by the commit date is commonly used, using `s` for snapshot is also common. ==== [[makefile-categories]] == Categorization [[makefile-categories-definition]] === `CATEGORIES` When a package is created, it is put under [.filename]#/usr/ports/packages/All# and links are made from one or more subdirectories of [.filename]#/usr/ports/packages#. The names of these subdirectories are specified by the variable `CATEGORIES`. It is intended to make life easier for the user when he is wading through the pile of packages on the FTP site or the CDROM. Please take a look at the <> and pick the ones that are suitable for the port. This list also determines where in the ports tree the port is imported. If there is more than one category here, the port files must be put in the subdirectory with the name of the first category. See <> for more discussion about how to pick the right categories. [[porting-categories]] === Current List of Categories Here is the current list of port categories. Those marked with an asterisk (`*`) are _virtual_ categories-those that do not have a corresponding subdirectory in the ports tree. They are only used as secondary categories, and only for search purposes. [NOTE] ==== For non-virtual categories, there is a one-line description in `COMMENT` in that subdirectory's [.filename]#Makefile#. ==== [.informaltable] [cols="1,1,1", frame="none", options="header"] |=== | Category | Description | Notes |[.filename]#accessibility# |Ports to help disabled users. | |[.filename]#afterstep#`*` |Ports to support the http://www.afterstep.org[AfterStep] window manager. | |[.filename]#arabic# |Arabic language support. | |[.filename]#archivers# |Archiving tools. | |[.filename]#astro# |Astronomical ports. | |[.filename]#audio# |Sound support. | |[.filename]#benchmarks# |Benchmarking utilities. | |[.filename]#biology# |Biology-related software. | |[.filename]#cad# |Computer aided design tools. | |[.filename]#chinese# |Chinese language support. | |[.filename]#comms# |Communication software. |Mostly software to talk to the serial port. |[.filename]#converters# |Character code converters. | |[.filename]#databases# |Databases. | |[.filename]#deskutils# |Things that used to be on the desktop before computers were invented. | |[.filename]#devel# |Development utilities. |Do not put libraries here just because they are libraries. They should _not_ be in this category unless they truly do not belong anywhere else. |[.filename]#dns# |DNS-related software. | |[.filename]#docs#`*` |Meta-ports for FreeBSD documentation. | |[.filename]#editors# |General editors. |Specialized editors go in the section for those tools. For example, a mathematical-formula editor will go in [.filename]#math#, and have [.filename]#editors# as a second category. |[.filename]#education#`*` |Education-related software. |This includes applications, utilities, or games primarily or substantially designed to help the user learn a specific topic or study in general. It also includes course-writing applications, course-delivery applications, and classroom or school management applications |[.filename]#elisp#`*` |Emacs-lisp ports. | |[.filename]#emulators# |Emulators for other operating systems. |Terminal emulators do _not_ belong here. X-based ones go to [.filename]#x11# and text-based ones to either [.filename]#comms# or [.filename]#misc#, depending on the exact functionality. |[.filename]#enlightenment#`*` |Ports related to the Enlightenment window manager. | |[.filename]#finance# |Monetary, financial and related applications. | |[.filename]#french# |French language support. | |[.filename]#ftp# |FTP client and server utilities. |If the port speaks both FTP and HTTP, put it in [.filename]#ftp# with a secondary category of [.filename]#www#. |[.filename]#games# |Games. | |[.filename]#geography#`*` |Geography-related software. | |[.filename]#german# |German language support. | |[.filename]#gnome#`*` |Ports from the http://www.gnome.org[GNOME] Project. | |[.filename]#gnustep#`*` |Software related to the GNUstep desktop environment. | |[.filename]#graphics# |Graphics utilities. | |[.filename]#hamradio#`*` |Software for amateur radio. | |[.filename]#haskell#`*` |Software related to the Haskell language. | |[.filename]#hebrew# |Hebrew language support. | |[.filename]#hungarian# |Hungarian language support. | |[.filename]#irc# |Internet Relay Chat utilities. | |[.filename]#japanese# |Japanese language support. | |[.filename]#java# |Software related to the Java(TM) language. |The [.filename]#java# category must not be the only one for a port. Save for ports directly related to the Java language, porters are also encouraged not to use [.filename]#java# as the main category of a port. |[.filename]#kde#`*` |Ports from the http://www.kde.org[KDE] Project (generic). | |[.filename]#kde-applications#`*` |Applications from the http://www.kde.org[KDE] Project. | |[.filename]#kde-frameworks#`*` |Add-on libraries from the http://www.kde.org[KDE] Project for programming with Qt. | |[.filename]#kde-plasma#`*` |Desktop from the http://www.kde.org[KDE] Project. | |[.filename]#kld#`*` |Kernel loadable modules. | |[.filename]#korean# |Korean language support. | |[.filename]#lang# |Programming languages. | |[.filename]#linux#`*` |Linux applications and support utilities. | |[.filename]#lisp#`*` |Software related to the Lisp language. | |[.filename]#mail# |Mail software. | |[.filename]#mate#`*` |Ports related to the MATE desktop environment, a fork of GNOME 2. | |[.filename]#math# |Numerical computation software and other utilities for mathematics. | |[.filename]#mbone#`*` |MBone applications. | |[.filename]#misc# |Miscellaneous utilities |Things that do not belong anywhere else. If at all possible, try to find a better category for the port than `misc`, as ports tend to be overlooked in here. |[.filename]#multimedia# |Multimedia software. | |[.filename]#net# |Miscellaneous networking software. | |[.filename]#net-im# |Instant messaging software. | |[.filename]#net-mgmt# |Networking management software. | |[.filename]#net-p2p# |Peer to peer network applications. | |[.filename]#net-vpn#`*` |Virtual Private Network applications. | |[.filename]#news# |USENET news software. | |[.filename]#parallel#`*` |Applications dealing with parallelism in computing. | |[.filename]#pear#`*` |Ports related to the Pear PHP framework. | |[.filename]#perl5#`*` |Ports that require Perl version 5 to run. | |[.filename]#plan9#`*` |Various programs from http://www.cs.bell-labs.com/plan9dist/[Plan9]. | |[.filename]#polish# |Polish language support. | |[.filename]#ports-mgmt# |Ports for managing, installing and developing FreeBSD ports and packages. | |[.filename]#portuguese# |Portuguese language support. | |[.filename]#print# |Printing software. |Desktop publishing tools (previewers, etc.) belong here too. |[.filename]#python#`*` |Software related to the http://www.python.org/[Python] language. | |[.filename]#ruby#`*` |Software related to the http://www.ruby-lang.org/[Ruby] language. | |[.filename]#rubygems#`*` |Ports of http://www.rubygems.org/[RubyGems] packages. | |[.filename]#russian# |Russian language support. | |[.filename]#scheme#`*` |Software related to the Scheme language. | |[.filename]#science# |Scientific ports that do not fit into other categories such as [.filename]#astro#, [.filename]#biology# and [.filename]#math#. | |[.filename]#security# |Security utilities. | |[.filename]#shells# |Command line shells. | |[.filename]#spanish#`*` |Spanish language support. | |[.filename]#sysutils# |System utilities. | |[.filename]#tcl#`*` |Ports that use Tcl to run. | |[.filename]#textproc# |Text processing utilities. |It does not include desktop publishing tools, which go to [.filename]#print#. |[.filename]#tk#`*` |Ports that use Tk to run. | |[.filename]#ukrainian# |Ukrainian language support. | |[.filename]#vietnamese# |Vietnamese language support. | |[.filename]#wayland#`*` |Ports to support the Wayland display server. | |[.filename]#windowmaker#`*` |Ports to support the Window Maker window manager. | |[.filename]#www# |Software related to the World Wide Web. |HTML language support belongs here too. |[.filename]#x11# |The X Window System and friends. |This category is only for software that directly supports the window system. Do not put regular X applications here. Most of them go into other [.filename]#x11-*# categories (see below). |[.filename]#x11-clocks# |X11 clocks. | |[.filename]#x11-drivers# |X11 drivers. | |[.filename]#x11-fm# |X11 file managers. | |[.filename]#x11-fonts# |X11 fonts and font utilities. | |[.filename]#x11-servers# |X11 servers. | |[.filename]#x11-themes# |X11 themes. | |[.filename]#x11-toolkits# |X11 toolkits. | |[.filename]#x11-wm# |X11 window managers. | |[.filename]#xfce#`*` |Ports related to the http://www.xfce.org/[Xfce] desktop environment. | |[.filename]#zope#`*` |http://www.zope.org/[Zope] support. | |=== [[choosing-categories]] === Choosing the Right Category As many of the categories overlap, choosing which of the categories will be the primary category of the port can be tedious. There are several rules that govern this issue. Here is the list of priorities, in decreasing order of precedence: * The first category must be a physical category (see <>). This is necessary to make the packaging work. Virtual categories and physical categories may be intermixed after that. * Language specific categories always come first. For example, if the port installs Japanese X11 fonts, then the `CATEGORIES` line would read [.filename]#japanese x11-fonts#. * Specific categories are listed before less-specific ones. For instance, an HTML editor is listed as [.filename]#www editors#, not the other way around. Also, do not list [.filename]#net# when the port belongs to any of [.filename]#irc#, [.filename]#mail#, [.filename]#news#, [.filename]#security#, or [.filename]#www#, as [.filename]#net# is included implicitly. * [.filename]#x11# is used as a secondary category only when the primary category is a natural language. In particular, do not put [.filename]#x11# in the category line for X applications. * Emacs modes are placed in the same ports category as the application supported by the mode, not in [.filename]#editors#. For example, an Emacs mode to edit source files of some programming language goes into [.filename]#lang#. * Ports installing loadable kernel modules also have the virtual category [.filename]#kld# in their `CATEGORIES` line. This is one of the things handled automatically by adding `USES=kmod`. * [.filename]#misc# does not appear with any other non-virtual category. If there is `misc` with something else in `CATEGORIES`, that means `misc` can safely be deleted and the port placed only in the other subdirectory. * If the port truly does not belong anywhere else, put it in [.filename]#misc#. If the category is not clearly defined, please put a comment to that effect in the https://bugs.freebsd.org/submit/[port submission] in the bug database so we can discuss it before we import it. As a committer, send a note to the {freebsd-ports} so we can discuss it first. Too often, new ports are imported to the wrong category only to be moved right away. [[proposing-categories]] === Proposing a New Category As the Ports Collection has grown over time, various new categories have been introduced. New categories can either be _virtual_ categories-those that do not have a corresponding subdirectory in the ports tree- or _physical_ categories-those that do. This section discusses the issues involved in creating a new physical category. Read it thouroughly before proposing a new one. Our existing practice has been to avoid creating a new physical category unless either a large number of ports would logically belong to it, or the ports that would belong to it are a logically distinct group that is of limited general interest (for instance, categories related to spoken human languages), or preferably both. The rationale for this is that such a change creates a extref:{committers-guide}[fair amount of work, ports] for both the committers and also for all users who track changes to the Ports Collection. In addition, proposed category changes just naturally seem to attract controversy. (Perhaps this is because there is no clear consensus on when a category is "too big", nor whether categories should lend themselves to browsing (and thus what number of categories would be an ideal number), and so forth.) Here is the procedure: [.procedure] . Propose the new category on {freebsd-ports}. Include a detailed rationale for the new category, including why the existing categories are not sufficient, and the list of existing ports proposed to move. (If there are new ports pending in Bugzilla that would fit this category, list them too.) If you are the maintainer and/or submitter, respectively, mention that as it may help the case. . Participate in the discussion. . If it seems that there is support for the idea, file a PR which includes both the rationale and the list of existing ports that need to be moved. Ideally, this PR would also include these patches: ** [.filename]##Makefile##s for the new ports once they are repocopied ** [.filename]#Makefile# for the new category ** [.filename]#Makefile# for the old ports' categories ** [.filename]##Makefile##s for ports that depend on the old ports ** (for extra credit, include the other files that have to change, as per the procedure in the Committer's Guide.) . Since it affects the ports infrastructure and involves moving and patching many ports but also possibly running regression tests on the build cluster, assign the PR to the {portmgr}. . If that PR is approved, a committer will need to follow the rest of the procedure that is extref:{committers-guide}[outlined in the Committer's Guide, ports]. Proposing a new virtual category is similar to the above but much less involved, since no ports will actually have to move. In this case, the only patches to include in the PR would be those to add the new category to `CATEGORIES` of the affected ports. [[proposing-reorg]] === Proposing Reorganizing All the Categories Occasionally someone proposes reorganizing the categories with either a 2-level structure, or some other kind of keyword structure. To date, nothing has come of any of these proposals because, while they are very easy to make, the effort involved to retrofit the entire existing ports collection with any kind of reorganization is daunting to say the very least. Please read the history of these proposals in the mailing list archives before posting this idea. Furthermore, be prepared to be challenged to offer a working prototype. [[makefile-distfiles]] == The Distribution Files The second part of the [.filename]#Makefile# describes the files that must be downloaded to build the port, and where they can be downloaded. [[makefile-distname]] === `DISTNAME` `DISTNAME` is the name of the port as called by the authors of the software. `DISTNAME` defaults to `${PORTNAME}-${DISTVERSIONPREFIX}${DISTVERSION}${DISTVERSIONSUFFIX}`, and if not set, `DISTVERSION` defaults to `${PORTVERSION}` so override `DISTNAME` only if necessary. `DISTNAME` is only used in two places. First, the distribution file list (`DISTFILES`) defaults to `${DISTNAME}${EXTRACT_SUFX}`. Second, the distribution file is expected to extract into a subdirectory named `WRKSRC`, which defaults to [.filename]#work/${DISTNAME}#. Some vendor's distribution names which do not fit into the `${PORTNAME}-${PORTVERSION}`-scheme can be handled automatically by setting `DISTVERSIONPREFIX`, `DISTVERSION`, and `DISTVERSIONSUFFIX`. `PORTVERSION` will be derived from `DISTVERSION` automatically. [IMPORTANT] ==== Only one of `PORTVERSION` and `DISTVERSION` can be set at a time. If `DISTVERSION` does not derive a correct `PORTVERSION`, do not use `DISTVERSION`. ==== If the upstream version scheme can be derived into a ports-compatible version scheme, set some variable to the upstream version, _do not_ use `DISTVERSION` as the variable name. Set `PORTVERSION` to the computed version based on the variable you created, and set `DISTNAME` accordingly. If the upstream version scheme cannot easily be coerced into a ports-compatible value, set `PORTVERSION` to a sensible value, and set `DISTNAME` with `PORTNAME` with the verbatim upstream version. [[makefile-distname-ex1]] .Deriving `PORTVERSION` Manually [example] ==== BIND9 uses a version scheme that is not compatible with the ports versions (it has `-` in its versions) and cannot be derived using `DISTVERSION` because after the 9.9.9 release, it will release a "patchlevels" in the form of `9.9.9-P1`. DISTVERSION would translate that into `9.9.9.p1`, which, in the ports versioning scheme means 9.9.9 pre-release 1, which is before 9.9.9 and not after. So `PORTVERSION` is manually derived from an `ISCVERSION` variable to output `9.9.9p1`. The order into which the ports framework, and pkg, will sort versions is checked using the `-t` argument of man:pkg-version[8]: [source,shell] .... % pkg version -t 9.9.9 9.9.9.p1 > <.> -% pkg version -t 9.9.9 9.9.9p1 +% pkg version -t 9.9.9 9.9.9p1 < <.> .... <.> The `>` sign means that the first argument passed to `-t` is greater than the second argument. `9.9.9` is after `9.9.9.p1`. <.> The `<` sign means that the first argument passed to `-t` is less than the second argument. `9.9.9` is before `9.9.9p1`. In the port [.filename]#Makefile#, for example package:dns/bind99[], it is achieved by: [.programlisting] .... PORTNAME= bind -PORTVERSION= ${ISCVERSION:S/-P/P/:S/b/.b/:S/a/.a/:S/rc/.rc/} +PORTVERSION= ${ISCVERSION:S/-P/P/:S/b/.b/:S/a/.a/:S/rc/.rc/} CATEGORIES= dns net -MASTER_SITES= ISC/bind9/${ISCVERSION} +MASTER_SITES= ISC/bind9/${ISCVERSION} PKGNAMESUFFIX= 99 -DISTNAME= ${PORTNAME}-${ISCVERSION} +DISTNAME= ${PORTNAME}-${ISCVERSION} MAINTAINER= mat@FreeBSD.org COMMENT= BIND DNS suite with updated DNSSEC and DNS64 LICENSE= ISCL # ISC releases things like 9.8.0-P1 or 9.8.1rc1, which our versioning does not like ISCVERSION= 9.9.9-P6 .... Define upstream version in `ISCVERSION`, with a comment saying _why_ it is needed. Use `ISCVERSION` to get a ports-compatible `PORTVERSION`. Use `ISCVERSION` directly to get the correct URL for fetching the distribution file. Use `ISCVERSION` directly to name the distribution file. ==== [[makefile-distname-ex2]] .Derive `DISTNAME` from `PORTVERSION` [example] ==== From time to time, the distribution file name has little or no relation to the version of the software. In package:comms/kermit[], only the last element of the version is present in the distribution file: [.programlisting] .... PORTNAME= kermit PORTVERSION= 9.0.304 CATEGORIES= comms ftp net MASTER_SITES= ftp://ftp.kermitproject.org/kermit/test/tar/ DISTNAME= cku${PORTVERSION:E}-dev20 .... The `:E` man:make[1] modifier returns the suffix of the variable, in this case, `304`. The distribution file is correctly generated as `cku304-dev20.tar.gz`. ==== [[makefile-distname-ex3]] .Exotic Case 1 [example] ==== Sometimes, there is no relation between the software name, its version, and the distribution file it is distributed in. From package:audio/libworkman[]: [.programlisting] .... PORTNAME= libworkman PORTVERSION= 1.4 CATEGORIES= audio MASTER_SITES= LOCAL/jim DISTNAME= ${PORTNAME}-1999-06-20 .... ==== [[makefile-distname-ex4]] .Exotic Case 2 [example] ==== In package:comms/librs232[], the distribution file is not versioned, so using <> is needed: [.programlisting] .... PORTNAME= librs232 PORTVERSION= 20160710 CATEGORIES= comms MASTER_SITES= http://www.teuniz.net/RS-232/ DISTNAME= RS-232 DIST_SUBDIR= ${PORTNAME}-${PORTVERSION} .... ==== [NOTE] ==== `PKGNAMEPREFIX` and `PKGNAMESUFFIX` do not affect `DISTNAME`. Also note that if `WRKSRC` is equal to [.filename]#${WRKDIR}/${DISTNAME}# while the original source archive is named something other than `${PORTNAME}-${PORTVERSION}${EXTRACT_SUFX}`, leave `DISTNAME` alone- defining only `DISTFILES` is easier than both `DISTNAME` and `WRKSRC` (and possibly `EXTRACT_SUFX`). ==== [[makefile-master_sites]] === `MASTER_SITES` Record the directory part of the FTP/HTTP-URL pointing at the original tarball in `MASTER_SITES`. Do not forget the trailing slash ([.filename]#/#)! The `make` macros will try to use this specification for grabbing the distribution file with `FETCH` if they cannot find it already on the system. It is recommended that multiple sites are included on this list, preferably from different continents. This will safeguard against wide-area network problems. [IMPORTANT] ==== `MASTER_SITES` must not be blank. It must point to the actual site hosting the distribution files. It cannot point to web archives, or the FreeBSD distribution files cache sites. The only exception to this rule is ports that do not have any distribution files. For example, meta-ports do not have any distribution files, so `MASTER_SITES` does not need to be set. ==== [[makefile-master_sites-shorthand]] ==== Using `MASTER_SITE_*` Variables Shortcut abbreviations are available for popular archives like SourceForge (`SOURCEFORGE`), GNU (`GNU`), or Perl CPAN (`PERL_CPAN`). `MASTER_SITES` can use them directly: [.programlisting] .... MASTER_SITES= GNU/make .... The older expanded format still works, but all ports have been converted to the compact format. The expanded format looks like this: [.programlisting] .... MASTER_SITES= ${MASTER_SITE_GNU} MASTER_SITE_SUBDIR= make .... These values and variables are defined in https://cgit.freebsd.org/ports/tree/Mk/bsd.sites.mk[Mk/bsd.sites.mk]. New entries are added often, so make sure to check the latest version of this file before submitting a port. [TIP] ==== For any `MASTER_SITE_FOO` variable, the shorthand `_FOO_` can be used. For example, use: [.programlisting] .... MASTER_SITES= FOO .... If `MASTER_SITE_SUBDIR` is needed, use this: [.programlisting] .... MASTER_SITES= FOO/bar .... ==== [NOTE] ==== Some `MASTER_SITE_*` names are quite long, and for ease of use, shortcuts have been defined: [[makefile-master_sites-shortcut]] .Shortcuts for `MASTER_SITE_*` Macros [cols="1,1", frame="none", options="header"] |=== | Macro | Shortcut |`PERL_CPAN` |`CPAN` |`GITHUB` |`GH` |`GITHUB_CLOUD` |`GHC` |`LIBREOFFICE_DEV` |`LODEV` |`NETLIB` |`NL` |`RUBYGEMS` |`RG` |`SOURCEFORGE` |`SF` |=== ==== [[makefile-master_sites-magic]] ==== Magic MASTER_SITES Macros Several "magic" macros exist for popular sites with a predictable directory structure. For these, just use the abbreviation and the system will choose a subdirectory automatically. For a port named `Stardict`, of version `1.2.3`, and hosted on SourceForge, adding this line: [.programlisting] .... MASTER_SITES= SF .... infers a subdirectory named `/project/stardict/stardict/1.2.3`. If the inferred directory is incorrect, it can be overridden: [.programlisting] .... MASTER_SITES= SF/stardict/WyabdcRealPeopleTTS/${PORTVERSION} .... This can also be written as [.programlisting] .... MASTER_SITES= SF MASTER_SITE_SUBDIR= stardict/WyabdcRealPeopleTTS/${PORTVERSION} .... [[makefile-master_sites-popular]] .Magic `MASTER_SITES` Macros [cols="1,1", frame="none", options="header"] |=== | Macro | Assumed subdirectory |`APACHE_COMMONS_BINARIES` |`${PORTNAME:S,commons-,,}` |`APACHE_COMMONS_SOURCE` |`${PORTNAME:S,commons-,,}` |`APACHE_JAKARTA` |`${PORTNAME:S,-,/,}/source` |`BERLIOS` |`${PORTNAME:tl}.berlios` |`CHEESESHOP` |`source/${DISTNAME:C/(.).\*/\1/}/${DISTNAME:C/(.*)-[0-9].*/\1/}` |`CPAN` |`${PORTNAME:C/-.*//}` |`DEBIAN` |`pool/main/${PORTNAME:C/^((lib)?.).*$/\1/}/${PORTNAME}` |`FARSIGHT` |`${PORTNAME}` |`FESTIVAL` |`${PORTREVISION}` |`GCC` |`releases/${DISTNAME}` |`GENTOO` |`distfiles` |`GIMP` |`${PORTNAME}/${PORTVERSION:R}/` |`GH` |`${GH_ACCOUNT}/${GH_PROJECT}/tar.gz/${GH_TAGNAME}?dummy=/` |`GHC` |`${GH_ACCOUNT}/${GH_PROJECT}/` |`GNOME` |`sources/${PORTNAME}/${PORTVERSION:C/^([0-9]+\.[0-9]+).*/\1/}` |`GNU` |`${PORTNAME}` |`GNUPG` |`${PORTNAME}` |`GNU_ALPHA` |`${PORTNAME}` |`HORDE` |`${PORTNAME}` |`LODEV` |`${PORTNAME}` |`MATE` |`${PORTVERSION:C/^([0-9]+\.[0-9]+).*/\1/}` |`MOZDEV` |`${PORTNAME:tl}` |`NL` |`${PORTNAME}` |`QT` |`archive/qt/${PORTVERSION:R}` |`SAMBA` |`${PORTNAME}` |`SAVANNAH` |`${PORTNAME:tl}` |`SF` |`${PORTNAME:tl}/${PORTNAME:tl}/${PORTVERSION}` |=== [[makefile-master_sites-github]] === `USE_GITHUB` If the distribution file comes from a specific commit or tag on https://github.com[GitHub] for which there is no officially released file, there is an easy way to set the right `DISTNAME` and `MASTER_SITES` automatically. These variables are available: [[makefile-master_sites-github-description]] .`USE_GITHUB` Description [cols="1,1,1", options="header"] |=== | Variable | Description | Default |`GH_ACCOUNT` |Account name of the GitHub user hosting the project |`${PORTNAME}` |`GH_PROJECT` |Name of the project on GitHub |`${PORTNAME}` |`GH_TAGNAME` |Name of the tag to download (2.0.1, hash, ...) Using the name of a branch here is incorrect. It is also possible to use the hash of a commit id to do a snapshot. |`${DISTVERSIONPREFIX}${DISTVERSION}${DISTVERSIONSUFFIX}` |`GH_SUBDIR` |When the software needs an additional distribution file to be extracted within `${WRKSRC}`, this variable can be used. See the examples in <> for more information. |(none) |`GH_TUPLE` |`GH_TUPLE` allows putting `GH_ACCOUNT`, `GH_PROJECT`, `GH_TAGNAME`, and `GH_SUBDIR` into a single variable. The format is _account_`:`_project_`:`_tagname_`:`_group_`/`_subdir_. The `/`_subdir_ part is optional. It is helpful when there is more than one GitHub project from which to fetch. |(none) |=== [IMPORTANT] ==== Do not use `GH_TUPLE` for the default distribution file, as it has no default. ==== [[makefile-master_sites-github-ex1]] .Simple Use of `USE_GITHUB` [example] ==== While trying to make a port for version `1.2.7` of pkg from the FreeBSD user on github, at https://github.com/freebsd/pkg[], The [.filename]#Makefile# would end up looking like this (slightly stripped for the example): [.programlisting] .... PORTNAME= pkg DISTVERSION= 1.2.7 USE_GITHUB= yes GH_ACCOUNT= freebsd .... It will automatically have `MASTER_SITES` set to `GH` and `WRKSRC` to `${WRKDIR}/pkg-1.2.7`. ==== [[makefile-master_sites-github-ex2]] .More Complete Use of `USE_GITHUB` [example] ==== While trying to make a port for the bleeding edge version of pkg from the FreeBSD user on github, at https://github.com/freebsd/pkg[], the [.filename]#Makefile# ends up looking like this (slightly stripped for the example): [.programlisting] .... PORTNAME= pkg-devel DISTVERSION= 1.3.0.a.20140411 USE_GITHUB= yes GH_ACCOUNT= freebsd GH_PROJECT= pkg GH_TAGNAME= 6dbb17b .... It will automatically have `MASTER_SITES` set to `GH` and `WRKSRC` to `${WRKDIR}/pkg-6dbb17b`. [TIP] -**** +==== `20140411` is the date of the commit referenced in `GH_TAGNAME`, not the date the [.filename]#Makefile# is edited, or the date the commit is made. -**** +==== ==== [[makefile-master_sites-github-ex3]] .Use of `USE_GITHUB` with `DISTVERSIONPREFIX` [example] ==== From time to time, `GH_TAGNAME` is a slight variation from `DISTVERSION`. For example, if the version is `1.0.2`, the tag is `v1.0.2`. In those cases, it is possible to use `DISTVERSIONPREFIX` or `DISTVERSIONSUFFIX`: [.programlisting] .... PORTNAME= foo DISTVERSIONPREFIX= v DISTVERSION= 1.0.2 USE_GITHUB= yes .... It will automatically set `GH_TAGNAME` to `v1.0.2`, while `WRKSRC` will be kept to `${WRKDIR}/foo-1.0.2`. ==== [[makefile-master_sites-github-ex4]] .Using `USE_GITHUB` When Upstream Does Not Use Versions [example] ==== If there never was a version upstream, do not invent one like `0.1` or `1.0`. Create the port with a `DISTVERSION` of `g__YYYYMMDD__`, where `g` is for Git, and `_YYYYMMDD_` represents the date the commit referenced in `GH_TAGNAME`. [.programlisting] .... PORTNAME= bar DISTVERSION= g20140411 USE_GITHUB= yes GH_TAGNAME= c472d66b .... This creates a versioning scheme that increases over time, and that is still before version `0` (see <> for details on man:pkg-version[8]): [source,shell] .... % pkg version -t g20140411 0 < .... Which means using `PORTEPOCH` will not be needed in case upstream decides to cut versions in the future. ==== [[makefile-master_sites-github-ex5]] .Using `USE_GITHUB` to Access a Commit Between Two Versions [example] ==== If the current version of the software uses a Git tag, and the port needs to be updated to a newer, intermediate version, without a tag, use man:git-describe[1] to find out the version to use: [source,shell] .... % git describe --tags f0038b1 v0.7.3-14-gf0038b1 .... `v0.7.3-14-gf0038b1` can be split into three parts: `v0.7.3`:: This is the last Git tag that appears in the commit history before the requested commit. `-14`:: This means that the requested commit, `f0038b1`, is the 14th commit after the `v0.7.3` tag. `-gf0038b1`:: The `-g` means "Git", and the `f0038b1` is the commit hash that this reference points to. [.programlisting] .... PORTNAME= bar DISTVERSIONPREFIX= v DISTVERSION= 0.7.3-14 DISTVERSIONSUFFIX= -gf0038b1 USE_GITHUB= yes .... This creates a versioning scheme that increases over time (well, over commits), and does not conflict with the creation of a `0.7.4` version. (See <> for details on man:pkg-version[8]): [source,shell] .... % pkg version -t 0.7.3 0.7.3.14 < % pkg version -t 0.7.3.14 0.7.4 < .... [NOTE] -**** +===== If the requested commit is the same as a tag, a shorter description is shown by default. The longer version is equivalent: [source,shell] .... % git describe --tags c66c71d v0.7.3 % git describe --tags --long c66c71d v0.7.3-0-gc66c71d .... -**** +===== ==== [[makefile-master_sites-github-multiple]] ==== Fetching Multiple Files from GitHub The `USE_GITHUB` framework also supports fetching multiple distribution files from different places in GitHub. It works in a way very similar to <>. Multiple values are added to `GH_ACCOUNT`, `GH_PROJECT`, and `GH_TAGNAME`. Each different value is assigned a group. The main value can either have no group, or the `:DEFAULT` group. A value can be omitted if it is the same as the default as listed in <>. `GH_TUPLE` can also be used when there are a lot of distribution files. It helps keep the account, project, tagname, and group information at the same place. For each group, a `${WRKSRC_group}` helper variable is created, containing the directory into which the file has been extracted. The `${WRKSRC_group}` variables can be used to move directories around during `post-extract`, or add to `CONFIGURE_ARGS`, or whatever is needed so that the software builds correctly. [CAUTION] ==== The `:__group__` part _must_ be used for _only one_ distribution file. It is used as a unique key and using it more than once will overwrite the previous values. ==== [NOTE] ==== As this is only syntactic sugar above `DISTFILES` and `MASTER_SITES`, the group names must adhere to the restrictions on group names outlined in <> ==== When fetching multiple files from GitHub, sometimes the default distribution file is not fetched from GitHub. To disable fetching the default distribution, set: [.programlisting] .... USE_GITHUB= nodefault .... [IMPORTANT] ==== When using `USE_GITHUB=nodefault`, the [.filename]#Makefile# must set `DISTFILES` in its crossref:porting-order[porting-order-portname,top block]. The definition should be: [.programlisting] .... DISTFILES= ${DISTNAME}${EXTRACT_SUFX} .... ==== [[makefile-master_sites-github-multi]] .Use of `USE_GITHUB` with Multiple Distribution Files [example] ==== From time to time, there is a need to fetch more than one distribution file. For example, when the upstream git repository uses submodules. This can be done easily using groups in the `GH_*` variables: [.programlisting] .... PORTNAME= foo DISTVERSION= 1.0.2 USE_GITHUB= yes GH_ACCOUNT= bar:icons,contrib GH_PROJECT= foo-icons:icons foo-contrib:contrib GH_TAGNAME= 1.0:icons fa579bc:contrib GH_SUBDIR= ext/icons:icons CONFIGURE_ARGS= --with-contrib=${WRKSRC_contrib} .... This will fetch three distribution files from github. The default one comes from [.filename]#foo/foo# and is version `1.0.2`. The second one, with the `icons` group, comes from [.filename]#bar/foo-icons# and is in version `1.0`. The third one comes from [.filename]#bar/foo-contrib# and uses the Git commit `fa579bc`. The distribution files are named [.filename]#foo-foo-1.0.2_GH0.tar.gz#, [.filename]#bar-foo-icons-1.0_GH0.tar.gz#, and [.filename]#bar-foo-contrib-fa579bc_GH0.tar.gz#. All the distribution files are extracted in `${WRKDIR}` in their respective subdirectories. The default file is still extracted in `${WRKSRC}`, in this case, [.filename]#${WRKDIR}/foo-1.0.2#. Each additional distribution file is extracted in `${WRKSRC_group}`. Here, for the `icons` group, it is called `${WRKSRC_icons}` and it contains [.filename]#${WRKDIR}/foo-icons-1.0#. The file with the `contrib` group is called `${WRKSRC_contrib}` and contains `${WRKDIR}/foo-contrib-fa579bc`. The software's build system expects to find the icons in a [.filename]#ext/icons# subdirectory in its sources, so `GH_SUBDIR` is used. `GH_SUBDIR` makes sure that [.filename]#ext# exists, but that [.filename]#ext/icons# does not already exist. Then it does this: [.programlisting] .... post-extract: @${MV} ${WRKSRC_icons} ${WRKSRC}/ext/icons .... ==== [[makefile-master_sites-github-multi2]] .Use of `USE_GITHUB` with Multiple Distribution Files Using `GH_TUPLE` [example] ==== This is functionally equivalent to <>, but using `GH_TUPLE`: [.programlisting] .... PORTNAME= foo DISTVERSION= 1.0.2 USE_GITHUB= yes GH_TUPLE= bar:foo-icons:1.0:icons/ext/icons \ bar:foo-contrib:fa579bc:contrib CONFIGURE_ARGS= --with-contrib=${WRKSRC_contrib} .... Grouping was used in the previous example with `bar:icons,contrib`. Some redundant information is present with `GH_TUPLE` because grouping is not possible. ==== [[makefile-master_sites-github-submodules]] .How to Use `USE_GITHUB` with Git Submodules? [example] ==== Ports with GitHub as an upstream repository sometimes use submodules. See man:git-submodule[1] for more information. The problem with submodules is that each is a separate repository. As such, they each must be fetched separately. Using package:finance/moneymanagerex[] as an example, its GitHub repository is https://github.com/moneymanagerex/moneymanagerex[]. It has a https://github.com/moneymanagerex/moneymanagerex/blob/master/.gitmodules[.gitmodules] file at the root. This file describes all the submodules used in this repository, and lists additional repositories needed. This file will tell what additional repositories are needed: [.programlisting] .... [submodule "lib/wxsqlite3"] path = lib/wxsqlite3 url = https://github.com/utelle/wxsqlite3.git [submodule "3rd/mongoose"] path = 3rd/mongoose url = https://github.com/cesanta/mongoose.git [submodule "3rd/LuaGlue"] path = 3rd/LuaGlue url = https://github.com/moneymanagerex/LuaGlue.git [submodule "3rd/cgitemplate"] path = 3rd/cgitemplate url = https://github.com/moneymanagerex/html-template.git [...] .... The only information missing from that file is the commit hash or tag to use as a version. This information is found after cloning the repository: [source,shell] .... % git clone --recurse-submodules https://github.com/moneymanagerex/moneymanagerex.git Cloning into 'moneymanagerex'... remote: Counting objects: 32387, done. [...] Submodule '3rd/LuaGlue' (https://github.com/moneymanagerex/LuaGlue.git) registered for path '3rd/LuaGlue' Submodule '3rd/cgitemplate' (https://github.com/moneymanagerex/html-template.git) registered for path '3rd/cgitemplate' Submodule '3rd/mongoose' (https://github.com/cesanta/mongoose.git) registered for path '3rd/mongoose' Submodule 'lib/wxsqlite3' (https://github.com/utelle/wxsqlite3.git) registered for path 'lib/wxsqlite3' [...] Cloning into '/home/mat/work/freebsd/ports/finance/moneymanagerex/moneymanagerex/3rd/LuaGlue'... Cloning into '/home/mat/work/freebsd/ports/finance/moneymanagerex/moneymanagerex/3rd/cgitemplate'... Cloning into '/home/mat/work/freebsd/ports/finance/moneymanagerex/moneymanagerex/3rd/mongoose'... Cloning into '/home/mat/work/freebsd/ports/finance/moneymanagerex/moneymanagerex/lib/wxsqlite3'... [...] Submodule path '3rd/LuaGlue': checked out 'c51d11a247ee4d1e9817dfa2a8da8d9e2f97ae3b' Submodule path '3rd/cgitemplate': checked out 'cd434eeeb35904ebcd3d718ba29c281a649b192c' Submodule path '3rd/mongoose': checked out '2140e5992ab9a3a9a34ce9a281abf57f00f95cda' Submodule path 'lib/wxsqlite3': checked out 'fb66eb230d8aed21dec273b38c7c054dcb7d6b51' [...] % cd moneymanagerex % git submodule status c51d11a247ee4d1e9817dfa2a8da8d9e2f97ae3b 3rd/LuaGlue (heads/master) cd434eeeb35904ebcd3d718ba29c281a649b192c 3rd/cgitemplate (cd434ee) 2140e5992ab9a3a9a34ce9a281abf57f00f95cda 3rd/mongoose (6.2-138-g2140e59) fb66eb230d8aed21dec273b38c7c054dcb7d6b51 lib/wxsqlite3 (v3.4.0) [...] .... It can also be found on GitHub. Each subdirectory that is a submodule is shown as `_directory @ hash_`, for example, `mongoose @ 2140e59`. [NOTE] -**** +===== While getting the information from GitHub seems more straightforward, the information found using `git submodule status` will provide more meaningful information. For example, here, ``lib/wxsqlite3``'s commit hash `fb66eb2` correspond to `v3.4.0`. Both can be used interchangeably, but when a tag is available, use it. -**** +===== Now that all the required information has been gathered, the [.filename]#Makefile# can be written (only GitHub-related lines are shown): [.programlisting] .... PORTNAME= moneymanagerex DISTVERSIONPREFIX= v DISTVERSION= 1.3.0 USE_GITHUB= yes GH_TUPLE= utelle:wxsqlite3:v3.4.0:wxsqlite3/lib/wxsqlite3 \ moneymanagerex:LuaGlue:c51d11a:lua_glue/3rd/LuaGlue \ moneymanagerex:html-template:cd434ee:html_template/3rd/cgitemplate \ cesanta:mongoose:2140e59:mongoose/3rd/mongoose \ [...] .... ==== [[makefile-master_sites-gitlab]] === `USE_GITLAB` Similar to GitHub, if the distribution file comes from https://gitlab.com[gitlab.com] or is hosting the GitLab software, these variables are available for use and might need to be set. [[makefile-master_sites-gitlab-description]] .`USE_GITLAB` Description [cols="1,1,1", options="header"] |=== | Variable | Description | Default |`GL_SITE` |Site name hosting the GitLab project |https://gitlab.com |`GL_ACCOUNT` |Account name of the GitLab user hosting the project |`${PORTNAME}` |`GL_PROJECT` |Name of the project on GitLab |`${PORTNAME}` |`GL_COMMIT` |The commit hash to download. Must be the full 160 bit, 40 character hex sha1 hash. This is a required variable for GitLab. |`(none)` |`GL_SUBDIR` |When the software needs an additional distribution file to be extracted within `${WRKSRC}`, this variable can be used. See the examples in <> for more information. |(none) |`GL_TUPLE` |`GL_TUPLE` allows putting `GL_SITE`, `GL_ACCOUNT`, `GL_PROJECT`, `GL_COMMIT`, and `GL_SUBDIR` into a single variable. The format is _site_`:`_account_`:`_project_`:`_commit_`:`_group_`/`_subdir_. The _site_`:` and `/`_subdir_ part is optional. It is helpful when there are more than one GitLab project from which to fetch. |(none) |=== [[makefile-master_sites-gitlab-ex1]] .Simple Use of `USE_GITLAB` [example] ==== While trying to make a port for version `1.14` of libsignon-glib from the accounts-sso user on gitlab.com, at https://gitlab.com/accounts-sso/libsignon-glib[], The [.filename]#Makefile# would end up looking like this for fetching the distribution files: [.programlisting] .... PORTNAME= libsignon-glib DISTVERSION= 1.14 USE_GITLAB= yes GL_ACCOUNT= accounts-sso GL_COMMIT= e90302e342bfd27bc8c9132ab9d0ea3d8723fd03 .... It will automatically have `MASTER_SITES` set to https://gitlab.com[gitlab.com] and `WRKSRC` to `${WRKDIR}/libsignon-glib-e90302e342bfd27bc8c9132ab9d0ea3d8723fd03-e90302e342bfd27bc8c9132ab9d0ea3d8723fd03`. ==== [[makefile-master_sites-gitlab-ex2]] .More Complete Use of `USE_GITLAB` [example] ==== A more complete use of the above if port had no versioning and foobar from the foo user on project bar on a self hosted GitLab site `https://gitlab.example.com`, the [.filename]#Makefile# ends up looking like this for fetching distribution files: [.programlisting] .... PORTNAME= foobar DISTVERSION= g20170906 USE_GITLAB= yes GL_SITE= https://gitlab.example.com GL_ACCOUNT= foo GL_PROJECT= bar GL_COMMIT= 9c1669ce60c3f4f5eb43df874d7314483fb3f8a6 .... It will have `MASTER_SITES` set to `"https://gitlab.example.com"` and `WRKSRC` to `${WRKDIR}/bar-9c1669ce60c3f4f5eb43df874d7314483fb3f8a6-9c1669ce60c3f4f5eb43df874d7314483fb3f8a6`. [TIP] ====== `20170906` is the date of the commit referenced in `GL_COMMIT`, not the date the [.filename]#Makefile# is edited, or the date the commit to the FreeBSD ports tree is made. ====== [NOTE] ====== ``GL_SITE``'s protocol, port and webroot can all be modified in the same variable. ====== ==== [[makefile-master_sites-gitlab-multiple]] ==== Fetching Multiple Files from GitLab The `USE_GITLAB` framework also supports fetching multiple distribution files from different places from GitLab and GitLab hosted sites. It works in a way very similar to <> and <>. Multiple values are added to `GL_SITE`, `GL_ACCOUNT`, `GL_PROJECT` and `GL_COMMIT`. Each different value is assigned a group. <>. `GL_TUPLE` can also be used when there are a lot of distribution files. It helps keep the site, account, project, commit, and group information at the same place. For each group, a `${WRKSRC_group}` helper variable is created, containing the directory into which the file has been extracted. The `${WRKSRC_group}` variables can be used to move directories around during `post-extract`, or add to `CONFIGURE_ARGS`, or whatever is needed so that the software builds correctly. [CAUTION] ==== The `:__group__` part _must_ be used for _only one_ distribution file. It is used as a unique key and using it more than once will overwrite the previous values. ==== [NOTE] ==== As this is only syntactic sugar above `DISTFILES` and `MASTER_SITES`, the group names must adhere to the restrictions on group names outlined in <> ==== When fetching multiple files using GitLab, sometimes the default distribution file is not fetched from a GitLab site. To disable fetching the default distribution, set: [.programlisting] .... USE_GITLAB= nodefault .... [IMPORTANT] ==== When using `USE_GITLAB=nodefault`, the [.filename]#Makefile# must set `DISTFILES` in its <>. The definition should be: [.programlisting] .... DISTFILES= ${DISTNAME}${EXTRACT_SUFX} .... ==== [[makefile-master_sites-gitlab-multi]] .Use of `USE_GITLAB` with Multiple Distribution Files [example] ==== From time to time, there is a need to fetch more than one distribution file. For example, when the upstream git repository uses submodules. This can be done easily using groups in the `GL_*` variables: [.programlisting] .... PORTNAME= foo DISTVERSION= 1.0.2 USE_GITLAB= yes GL_SITE= https://gitlab.example.com:9434/gitlab:icons GL_ACCOUNT= bar:icons,contrib GL_PROJECT= foo-icons:icons foo-contrib:contrib GL_COMMIT= c189207a55da45305c884fe2b50e086fcad4724b ae7368cab1ca7ca754b38d49da064df87968ffe4:icons 9e4dd76ad9b38f33fdb417a4c01935958d5acd2a:contrib GL_SUBDIR= ext/icons:icons CONFIGURE_ARGS= --with-contrib=${WRKSRC_contrib} .... This will fetch two distribution files from gitlab.com and one from `gitlab.example.com` hosting GitLab. The default one comes from [.filename]#https://gitlab.com/foo/foo# and commit is `c189207a55da45305c884fe2b50e086fcad4724b`. The second one, with the `icons` group, comes from [.filename]#https://gitlab.example.com:9434/gitlab/bar/foo-icons# and commit is `ae7368cab1ca7ca754b38d49da064df87968ffe4`. The third one comes from [.filename]#https://gitlab.com/bar/foo-contrib# and is commit `9e4dd76ad9b38f33fdb417a4c01935958d5acd2a`. The distribution files are named [.filename]#foo-foo-c189207a55da45305c884fe2b50e086fcad4724b_GL0.tar.gz#, [.filename]#bar-foo-icons-ae7368cab1ca7ca754b38d49da064df87968ffe4_GL0.tar.gz#, and [.filename]#bar-foo-contrib-9e4dd76ad9b38f33fdb417a4c01935958d5acd2a_GL0.tar.gz#. All the distribution files are extracted in `${WRKDIR}` in their respective subdirectories. The default file is still extracted in `${WRKSRC}`, in this case, [.filename]#${WRKDIR}/foo-c189207a55da45305c884fe2b50e086fcad4724b-c189207a55da45305c884fe2b50e086fcad4724b#. Each additional distribution file is extracted in `${WRKSRC_group}`. Here, for the `icons` group, it is called `${WRKSRC_icons}` and it contains [.filename]#${WRKDIR}/foo-icons-ae7368cab1ca7ca754b38d49da064df87968ffe4-ae7368cab1ca7ca754b38d49da064df87968ffe4#. The file with the `contrib` group is called `${WRKSRC_contrib}` and contains `${WRKDIR}/foo-contrib-9e4dd76ad9b38f33fdb417a4c01935958d5acd2a-9e4dd76ad9b38f33fdb417a4c01935958d5acd2a`. The software's build system expects to find the icons in a [.filename]#ext/icons# subdirectory in its sources, so `GL_SUBDIR` is used. `GL_SUBDIR` makes sure that [.filename]#ext# exists, but that [.filename]#ext/icons# does not already exist. Then it does this: [.programlisting] .... post-extract: @${MV} ${WRKSRC_icons} ${WRKSRC}/ext/icons .... ==== [[makefile-master_sites-gitlab-multi2]] .Use of `USE_GITLAB` with Multiple Distribution Files Using `GL_TUPLE` [example] ==== This is functionally equivalent to <>, but using `GL_TUPLE`: [.programlisting] .... PORTNAME= foo DISTVERSION= 1.0.2 USE_GITLAB= yes GL_COMMIT= c189207a55da45305c884fe2b50e086fcad4724b GL_TUPLE= https://gitlab.example.com:9434/gitlab:bar:foo-icons:ae7368cab1ca7ca754b38d49da064df87968ffe4:icons/ext/icons \ bar:foo-contrib:9e4dd76ad9b38f33fdb417a4c01935958d5acd2a:contrib CONFIGURE_ARGS= --with-contrib=${WRKSRC_contrib} .... Grouping was used in the previous example with `bar:icons,contrib`. Some redundant information is present with `GL_TUPLE` because grouping is not possible. ==== [[makefile-extract_sufx]] === `EXTRACT_SUFX` If there is one distribution file, and it uses an odd suffix to indicate the compression mechanism, set `EXTRACT_SUFX`. For example, if the distribution file was named [.filename]#foo.tar.gzip# instead of the more normal [.filename]#foo.tar.gz#, write: [.programlisting] .... DISTNAME= foo EXTRACT_SUFX= .tar.gzip .... The `USES=tar[:__xxx__]`, `USES=lha` or `USES=zip` automatically set `EXTRACT_SUFX` to the most common archives extensions as necessary, see crossref:uses[uses,Using `USES` Macros] for more details. If neither of these are set then `EXTRACT_SUFX` defaults to `.tar.gz`. [NOTE] ==== As `EXTRACT_SUFX` is only used in `DISTFILES`, only set one of them.. ==== [[makefile-distfiles-definition]] === `DISTFILES` Sometimes the names of the files to be downloaded have no resemblance to the name of the port. For example, it might be called [.filename]#source.tar.gz# or similar. In other cases the application's source code might be in several different archives, all of which must be downloaded. If this is the case, set `DISTFILES` to be a space separated list of all the files that must be downloaded. [.programlisting] .... DISTFILES= source1.tar.gz source2.tar.gz .... If not explicitly set, `DISTFILES` defaults to `${DISTNAME}${EXTRACT_SUFX}`. [[makefile-extract_only]] === `EXTRACT_ONLY` If only some of the `DISTFILES` must be extracted-for example, one of them is the source code, while another is an uncompressed document-list the filenames that must be extracted in `EXTRACT_ONLY`. [.programlisting] .... DISTFILES= source.tar.gz manual.html EXTRACT_ONLY= source.tar.gz .... When none of the `DISTFILES` need to be uncompressed, set `EXTRACT_ONLY` to the empty string. [.programlisting] .... EXTRACT_ONLY= .... [[porting-patchfiles]] === `PATCHFILES` If the port requires some additional patches that are available by FTP or HTTP, set `PATCHFILES` to the names of the files and `PATCH_SITES` to the URL of the directory that contains them (the format is the same as `MASTER_SITES`). If the patch is not relative to the top of the source tree (that is, `WRKSRC`) because it contains some extra pathnames, set `PATCH_DIST_STRIP` accordingly. For instance, if all the pathnames in the patch have an extra `foozolix-1.0/` in front of the filenames, then set `PATCH_DIST_STRIP=-p1`. Do not worry if the patches are compressed; they will be decompressed automatically if the filenames end with [.filename]#.Z#, [.filename]#.gz#, [.filename]#.bz2# or [.filename]#.xz#. If the patch is distributed with some other files, such as documentation, in a compressed tarball, using `PATCHFILES` is not possible. If that is the case, add the name and the location of the patch tarball to `DISTFILES` and `MASTER_SITES`. Then, use `EXTRA_PATCHES` to point to those files and [.filename]#bsd.port.mk# will automatically apply them. In particular, do _not_ copy patch files into [.filename]#${PATCHDIR}#. That directory may not be writable. [TIP] ==== If there are multiple patches and they need mixed values for the strip parameter, it can be added alongside the patch name in `PATCHFILES`, e.g: [.programlisting] .... PATCHFILES= patch1 patch2:-p1 .... This does not conflict with <>, adding a group also works: [.programlisting] .... PATCHFILES= patch2:-p1:source2 .... ==== [NOTE] ==== The tarball will have been extracted alongside the regular source by then, so there is no need to explicitly extract it if it is a regular compressed tarball. Take extra care not to overwrite something that already exists in that directory if extracting it manually. Also, do not forget to add a command to remove the copied patch in the `pre-clean` target. ==== [[porting-master-sites-n]] === Multiple Distribution or Patches Files from Multiple Locations (Consider this to be a somewhat "advanced topic"; those new to this document may wish to skip this section at first). This section has information on the fetching mechanism known as both `MASTER_SITES:n` and `MASTER_SITES_NN`. We will refer to this mechanism as `MASTER_SITES:n`. A little background first. OpenBSD has a neat feature inside `DISTFILES` and `PATCHFILES` which allows files and patches to be postfixed with `:n` identifiers. Here, `n` can be any word containing `[0-9a-zA-Z_]` and denote a group designation. For example: [.programlisting] .... DISTFILES= alpha:0 beta:1 .... In OpenBSD, distribution file [.filename]#alpha# will be associated with variable `MASTER_SITES0` instead of our common `MASTER_SITES` and [.filename]#beta# with `MASTER_SITES1`. This is a very interesting feature which can decrease that endless search for the correct download site. Just picture 2 files in `DISTFILES` and 20 sites in `MASTER_SITES`, the sites slow as hell where [.filename]#beta# is carried by all sites in `MASTER_SITES`, and [.filename]#alpha# can only be found in the 20th site. It would be such a waste to check all of them if the maintainer knew this beforehand, would it not? Not a good start for that lovely weekend! Now that you have the idea, just imagine more `DISTFILES` and more `MASTER_SITES`. Surely our "distfiles survey meister" would appreciate the relief to network strain that this would bring. In the next sections, information will follow on the FreeBSD implementation of this idea. We improved a bit on OpenBSD's concept. [IMPORTANT] ==== The group names cannot have dashes in them (`-`), in fact, they cannot have any characters out of the `[a-zA-Z0-9_]` range. This is because, while man:make[1] is ok with variable names containing dashes, man:sh[1] is not. ==== [[porting-master-sites-n-simplified]] ==== Simplified Information This section explains how to quickly prepare fine grained fetching of multiple distribution files and patches from different sites and subdirectories. We describe here a case of simplified `MASTER_SITES:n` usage. This will be sufficient for most scenarios. More detailed information are available in <>. Some applications consist of multiple distribution files that must be downloaded from a number of different sites. For example, Ghostscript consists of the core of the program, and then a large number of driver files that are used depending on the user's printer. Some of these driver files are supplied with the core, but many others must be downloaded from a variety of different sites. To support this, each entry in `DISTFILES` may be followed by a colon and a "group name". Each site listed in `MASTER_SITES` is then followed by a colon, and the group that indicates which distribution files are downloaded from this site. For example, consider an application with the source split in two parts, [.filename]#source1.tar.gz# and [.filename]#source2.tar.gz#, which must be downloaded from two different sites. The port's [.filename]#Makefile# would include lines like <>. [[ports-master-sites-n-example-simple-use-one-file-per-site]] .Simplified Use of `MASTER_SITES:n` with One File Per Site [example] ==== [.programlisting] .... MASTER_SITES= ftp://ftp1.example.com/:source1 \ http://www.example.com/:source2 DISTFILES= source1.tar.gz:source1 \ source2.tar.gz:source2 .... ==== Multiple distribution files can have the same group. Continuing the previous example, suppose that there was a third distfile, [.filename]#source3.tar.gz#, that is downloaded from `ftp.example2.com`. The [.filename]#Makefile# would then be written like <>. [[ports-master-sites-n-example-simple-use-more-than-one-file-per-site]] .Simplified Use of `MASTER_SITES:n` with More Than One File Per Site [example] ==== [.programlisting] .... MASTER_SITES= ftp://ftp.example.com/:source1 \ http://www.example.com/:source2 DISTFILES= source1.tar.gz:source1 \ source2.tar.gz:source2 \ source3.tar.gz:source2 .... ==== [[ports-master-sites-n-detailed]] ==== Detailed Information Okay, so the previous example did not reflect the new port's needs? In this section we will explain in detail how the fine grained fetching mechanism `MASTER_SITES:n` works and how it can be used. . Elements can be postfixed with `:__n__` where _n_ is `[^:,]+`, that is, _n_ could conceptually be any alphanumeric string but we will limit it to `[a-zA-Z_][0-9a-zA-Z_]+` for now. + Moreover, string matching is case sensitive; that is, `n` is different from `N`. + However, these words cannot be used for postfixing purposes since they yield special meaning: `default`, `all` and `ALL` (they are used internally in item <>). Furthermore, `DEFAULT` is a special purpose word (check item <>). . Elements postfixed with `:n` belong to the group `n`, `:m` belong to group `m` and so forth. + [[porting-master-sites-n-DEFAULT-group]] . Elements without a postfix are groupless, they all belong to the special group `DEFAULT`. Any elements postfixed with `DEFAULT`, is just being redundant unless an element belongs to both `DEFAULT` and other groups at the same time (check item <>). + These examples are equivalent but the first one is preferred: + [.programlisting] .... MASTER_SITES= alpha .... + [.programlisting] .... MASTER_SITES= alpha:DEFAULT .... . Groups are not exclusive, an element may belong to several different groups at the same time and a group can either have either several different elements or none at all. + [[porting-master-sites-n-comma-operator]] . When an element belongs to several groups at the same time, use the comma operator (`,`). + Instead of repeating it several times, each time with a different postfix, we can list several groups at once in a single postfix. For instance, `:m,n,o` marks an element that belongs to group `m`, `n` and `o`. + All these examples are equivalent but the last one is preferred: + [.programlisting] .... MASTER_SITES= alpha alpha:SOME_SITE .... + [.programlisting] .... MASTER_SITES= alpha:DEFAULT alpha:SOME_SITE .... + [.programlisting] .... MASTER_SITES= alpha:SOME_SITE,DEFAULT .... + [.programlisting] .... MASTER_SITES= alpha:DEFAULT,SOME_SITE .... . All sites within a given group are sorted according to `MASTER_SORT_AWK`. All groups within `MASTER_SITES` and `PATCH_SITES` are sorted as well. + [[porting-master-sites-n-group-semantics]] . Group semantics can be used in any of the variables `MASTER_SITES`, `PATCH_SITES`, `MASTER_SITE_SUBDIR`, `PATCH_SITE_SUBDIR`, `DISTFILES`, and `PATCHFILES` according to this syntax: .. All `MASTER_SITES`, `PATCH_SITES`, `MASTER_SITE_SUBDIR` and `PATCH_SITE_SUBDIR` elements must be terminated with the forward slash `/` character. If any elements belong to any groups, the group postfix `:__n__` must come right after the terminator `/`. The `MASTER_SITES:n` mechanism relies on the existence of the terminator `/` to avoid confusing elements where a `:n` is a valid part of the element with occurrences where `:n` denotes group `n`. For compatibility purposes, since the `/` terminator was not required before in both `MASTER_SITE_SUBDIR` and `PATCH_SITE_SUBDIR` elements, if the postfix immediate preceding character is not a `/` then `:n` will be considered a valid part of the element instead of a group postfix even if an element is postfixed with `:n`. See both <> and <>. + [[ports-master-sites-n-example-detailed-use-master-site-subdir]] .Detailed Use of `MASTER_SITES:n` in `MASTER_SITE_SUBDIR` [example] ==== [.programlisting] .... MASTER_SITE_SUBDIR= old:n new/:NEW .... *** Directories within group `DEFAULT` -> old:n *** Directories within group `NEW` -> new ==== + [[ports-master-sites-n-example-detailed-use-complete-example-master-sites]] .Detailed Use of `MASTER_SITES:n` with Comma Operator, Multiple Files, Multiple Sites and Multiple Subdirectories [example] ==== [.programlisting] .... MASTER_SITES= http://site1/%SUBDIR%/ http://site2/:DEFAULT \ http://site3/:group3 http://site4/:group4 \ http://site5/:group5 http://site6/:group6 \ http://site7/:DEFAULT,group6 \ http://site8/%SUBDIR%/:group6,group7 \ http://site9/:group8 DISTFILES= file1 file2:DEFAULT file3:group3 \ file4:group4,group5,group6 file5:grouping \ file6:group7 MASTER_SITE_SUBDIR= directory-trial:1 directory-n/:groupn \ directory-one/:group6,DEFAULT \ directory .... The previous example results in this fine grained fetching. Sites are listed in the exact order they will be used. *** [.filename]#file1# will be fetched from **** `MASTER_SITE_OVERRIDE` **** http://site1/directory-trial:1/ **** http://site1/directory-one/ **** http://site1/directory/ **** http://site2/ **** http://site7/ **** `MASTER_SITE_BACKUP` *** [.filename]#file2# will be fetched exactly as [.filename]#file1# since they both belong to the same group **** `MASTER_SITE_OVERRIDE` **** http://site1/directory-trial:1/ **** http://site1/directory-one/ **** http://site1/directory/ **** http://site2/ **** http://site7/ **** `MASTER_SITE_BACKUP` *** [.filename]#file3# will be fetched from **** `MASTER_SITE_OVERRIDE` **** http://site3/ **** `MASTER_SITE_BACKUP` *** [.filename]#file4# will be fetched from **** `MASTER_SITE_OVERRIDE` **** http://site4/ **** http://site5/ **** http://site6/ **** http://site7/ **** http://site8/directory-one/ **** `MASTER_SITE_BACKUP` *** [.filename]#file5# will be fetched from **** `MASTER_SITE_OVERRIDE` **** `MASTER_SITE_BACKUP` *** [.filename]#file6# will be fetched from **** `MASTER_SITE_OVERRIDE` **** http://site8/ **** `MASTER_SITE_BACKUP` ==== . How do I group one of the special macros from [.filename]#bsd.sites.mk#, for example, SourceForge (`SF`)? + This has been simplified as much as possible. See <>. + [[ports-master-sites-n-example-detailed-use-master-site-sourceforge]] .Detailed Use of `MASTER_SITES:n` with SourceForge (`SF`) [example] ==== [.programlisting] .... MASTER_SITES= http://site1/ SF/something/1.0:sourceforge,TEST DISTFILES= something.tar.gz:sourceforge .... [.filename]#something.tar.gz# will be fetched from all sites within SourceForge. ==== . How do I use this with `PATCH*`? + All examples were done with `MASTER*` but they work exactly the same for `PATCH*` ones as can be seen in <>. + [[ports-master-sites-n-example-detailed-use-patch-sites]] .Simplified Use of `MASTER_SITES:n` with `PATCH_SITES` [example] ==== [.programlisting] .... PATCH_SITES= http://site1/ http://site2/:test PATCHFILES= patch1:test .... ==== [[port-master-sites-n-what-changed]] ==== What Does Change for Ports? What Does Not? [lowerroman] . All current ports remain the same. The `MASTER_SITES:n` feature code is only activated if there are elements postfixed with `:__n__` like elements according to the aforementioned syntax rules, especially as shown in item <>. + [[porting-master-sites-n-what-changes-in-port-targets]] . The port targets remain the same: `checksum`, `makesum`, `patch`, `configure`, `build`, etc. With the obvious exceptions of `do-fetch`, `fetch-list`, `master-sites` and `patch-sites`. ** `do-fetch`: deploys the new grouping postfixed `DISTFILES` and `PATCHFILES` with their matching group elements within both `MASTER_SITES` and `PATCH_SITES` which use matching group elements within both `MASTER_SITE_SUBDIR` and `PATCH_SITE_SUBDIR`. Check <>. ** `fetch-list`: works like old `fetch-list` with the exception that it groups just like `do-fetch`. ** `master-sites` and `patch-sites`: (incompatible with older versions) only return the elements of group `DEFAULT`; in fact, they execute targets `master-sites-default` and `patch-sites-default` respectively. + Furthermore, using target either `master-sites-all` or `patch-sites-all` is preferred to directly checking either `MASTER_SITES` or `PATCH_SITES`. Also, directly checking is not guaranteed to work in any future versions. Check item <> for more information on these new port targets. . New port targets .. There are `master-sites-_n_` and `patch-sites-_n_` targets which will list the elements of the respective group _n_ within `MASTER_SITES` and `PATCH_SITES` respectively. For instance, both `master-sites-DEFAULT` and `patch-sites-DEFAULT` will return the elements of group `DEFAULT`, `master-sites-test` and `patch-sites-test` of group `test`, and thereon. + [[porting-master-sites-n-new-port-targets-master-sites-all]] .. There are new targets `master-sites-all` and `patch-sites-all` which do the work of the old `master-sites` and `patch-sites` ones. They return the elements of all groups as if they all belonged to the same group with the caveat that it lists as many `MASTER_SITE_BACKUP` and `MASTER_SITE_OVERRIDE` as there are groups defined within either `DISTFILES` or `PATCHFILES`; respectively for `master-sites-all` and `patch-sites-all`. [[makefile-dist_subdir]] === `DIST_SUBDIR` Do not let the port clutter [.filename]#/usr/ports/distfiles#. If the port requires a lot of files to be fetched, or contains a file that has a name that might conflict with other ports (for example, [.filename]#Makefile#), set `DIST_SUBDIR` to the name of the port (`${PORTNAME}` or `${PKGNAMEPREFIX}${PORTNAME}` are fine). This will change `DISTDIR` from the default [.filename]#/usr/ports/distfiles# to [.filename]#/usr/ports/distfiles/${DIST_SUBDIR}#, and in effect puts everything that is required for the port into that subdirectory. It will also look at the subdirectory with the same name on the backup master site at http://distcache.FreeBSD.org[http://distcache.FreeBSD.org] (Setting `DISTDIR` explicitly in [.filename]#Makefile# will not accomplish this, so please use `DIST_SUBDIR`.) [NOTE] ==== This does not affect `MASTER_SITES` defined in the [.filename]#Makefile#. ==== [[makefile-maintainer]] == `MAINTAINER` Set your mail-address here. Please. _:-)_ Only a single address without the comment part is allowed as a `MAINTAINER` value. The format used is `user@hostname.domain`. Please do not include any descriptive text such as a real name in this entry. That merely confuses the Ports infrastructure and most tools using it. The maintainer is responsible for keeping the port up to date and making sure that it works correctly. For a detailed description of the responsibilities of a port maintainer, refer to extref:{contributing}[The challenge for port maintainers, maintain-port]. [NOTE] ==== A maintainer volunteers to keep a port in good working order. Maintainers have the primary responsibility for their ports, but not exclusive ownership. Ports exist for the benefit of the community and, in reality, belong to the community. What this means is that people other than the maintainer can make changes to a port. Large changes to the Ports Collection might require changes to many ports. The FreeBSD Ports Management Team or members of other teams might modify ports to fix dependency issues or other problems, like a version bump for a shared library update. Some types of fixes have "blanket approval" from the {portmgr}, allowing any committer to fix those categories of problems on any port. These fixes do not need approval from the maintainer. Blanket approval for most ports applies to fixes like infrastructure changes, or trivial and _tested_ build and runtime fixes. The current list is available in extref:{committers-guide}[Ports section of the Committer's Guide, ports-qa-misc-blanket-approval]. ==== Other changes to the port will be sent to the maintainer for review and approval before being committed. If the maintainer does not respond to an update request after two weeks (excluding major public holidays), then that is considered a maintainer timeout, and the update can be made without explicit maintainer approval. If the maintainer does not respond within three months, or if there have been three consecutive timeouts, then that maintainer is considered absent without leave, and all of their ports can be assigned back to the pool. Exceptions to this are anything maintained by the {portmgr}, or the {security-officer}. No unauthorized commits may ever be made to ports maintained by those groups. We reserve the right to modify the maintainer's submission to better match existing policies and style of the Ports Collection without explicit blessing from the submitter or the maintainer. Also, large infrastructural changes can result in a port being modified without the maintainer's consent. These kinds of changes will never affect the port's functionality. The {portmgr} reserves the right to revoke or override anyone's maintainership for any reason, and the {security-officer} reserves the right to revoke or override maintainership for security reasons. [[makefile-comment]] == `COMMENT` The comment is a one-line description of a port shown by `pkg info`. Please follow these rules when composing it: . The COMMENT string should be 70 characters or less. . Do _not_ include the package name or version number of software. . The comment must begin with a capital and end without a period. . Do not start with an indefinite article (that is, A or An). . Capitalize names such as Apache, JavaScript, or Perl. . Use a serial comma for lists of words: "green, red, and blue." . Check for spelling errors. Here is an example: [.programlisting] .... COMMENT= Cat chasing a mouse all over the screen .... The COMMENT variable immediately follows the MAINTAINER variable in the [.filename]#Makefile#. [[licenses]] == Licenses Each port must document the license under which it is available. If it is not an OSI approved license it must also document any restrictions on redistribution. [[licenses-license]] === `LICENSE` A short name for the license or licenses if more than one license apply. If it is one of the licenses listed in <>, only `LICENSE_FILE` and `LICENSE_DISTFILES` variables can be set. If this is a license that has not been defined in the ports framework (see <>), the `LICENSE_PERMS` and `LICENSE_NAME` must be set, along with either `LICENSE_FILE` or `LICENSE_TEXT`. `LICENSE_DISTFILES` and `LICENSE_GROUPS` can also be set, but are not required. The predefined licenses are shown in <>. The current list is always available in [.filename]#Mk/bsd.licenses.db.mk#. [[licenses-license-ex1]] .Simplest Usage, Predefined Licenses [example] ==== When the [.filename]#README# of some software says "This software is under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version." but does not provide the license file, use this: [.programlisting] .... LICENSE= LGPL21+ .... When the software provides the license file, use this: [.programlisting] .... LICENSE= LGPL21+ LICENSE_FILE= ${WRKSRC}/COPYING .... ==== For the predefined licenses, the default permissions are `dist-mirror dist-sell pkg-mirror pkg-sell auto-accept`. [[licenses-license-list]] .Predefined License List [cols="1,1,1,1", frame="none", options="header"] |=== | Short Name | Name | Group | Permissions |`AGPLv3` |GNU Affero General Public License version 3 |`FSF GPL OSI` |(default) |`AGPLv3+` |GNU Affero General Public License version 3 (or later) |`FSF GPL OSI` |(default) |`APACHE10` |Apache License 1.0 |`FSF` |(default) |`APACHE11` |Apache License 1.1 |`FSF OSI` |(default) |`APACHE20` |Apache License 2.0 |`FSF OSI` |(default) |`ART10` |Artistic License version 1.0 |`OSI` |(default) |`ART20` |Artistic License version 2.0 |`FSF GPL OSI` |(default) |`ARTPERL10` |Artistic License (perl) version 1.0 |`OSI` |(default) |`BSD` |BSD license Generic Version (deprecated) |`FSF OSI COPYFREE` |(default) |`BSD2CLAUSE` |BSD 2-clause "Simplified" License |`FSF OSI COPYFREE` |(default) |`BSD3CLAUSE` |BSD 3-clause "New" or "Revised" License |`FSF OSI COPYFREE` |(default) |`BSD4CLAUSE` |BSD 4-clause "Original" or "Old" License |`FSF` |(default) |`BSL` |Boost Software License |`FSF OSI COPYFREE` |(default) |`CC-BY-1.0` |Creative Commons Attribution 1.0 | |(default) |`CC-BY-2.0` |Creative Commons Attribution 2.0 | |(default) |`CC-BY-2.5` |Creative Commons Attribution 2.5 | |(default) |`CC-BY-3.0` |Creative Commons Attribution 3.0 | |(default) |`CC-BY-4.0` |Creative Commons Attribution 4.0 | |(default) |`CC-BY-NC-1.0` |Creative Commons Attribution Non Commercial 1.0 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-2.0` |Creative Commons Attribution Non Commercial 2.0 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-2.5` |Creative Commons Attribution Non Commercial 2.5 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-3.0` |Creative Commons Attribution Non Commercial 3.0 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-4.0` |Creative Commons Attribution Non Commercial 4.0 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-ND-1.0` |Creative Commons Attribution Non Commercial No Derivatives 1.0 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-ND-2.0` |Creative Commons Attribution Non Commercial No Derivatives 2.0 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-ND-2.5` |Creative Commons Attribution Non Commercial No Derivatives 2.5 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-ND-3.0` |Creative Commons Attribution Non Commercial No Derivatives 3.0 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-ND-4.0` |Creative Commons Attribution Non Commercial No Derivatives 4.0 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-SA-1.0` |Creative Commons Attribution Non Commercial Share Alike 1.0 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-SA-2.0` |Creative Commons Attribution Non Commercial Share Alike 2.0 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-SA-2.5` |Creative Commons Attribution Non Commercial Share Alike 2.5 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-SA-3.0` |Creative Commons Attribution Non Commercial Share Alike 3.0 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-NC-SA-4.0` |Creative Commons Attribution Non Commercial Share Alike 4.0 | |`dist-mirror``pkg-mirror``auto-accept` |`CC-BY-ND-1.0` |Creative Commons Attribution No Derivatives 1.0 | |(default) |`CC-BY-ND-2.0` |Creative Commons Attribution No Derivatives 2.0 | |(default) |`CC-BY-ND-2.5` |Creative Commons Attribution No Derivatives 2.5 | |(default) |`CC-BY-ND-3.0` |Creative Commons Attribution No Derivatives 3.0 | |(default) |`CC-BY-ND-4.0` |Creative Commons Attribution No Derivatives 4.0 | |(default) |`CC-BY-SA-1.0` |Creative Commons Attribution Share Alike 1.0 | |(default) |`CC-BY-SA-2.0` |Creative Commons Attribution Share Alike 2.0 | |(default) |`CC-BY-SA-2.5` |Creative Commons Attribution Share Alike 2.5 | |(default) |`CC-BY-SA-3.0` |Creative Commons Attribution Share Alike 3.0 | |(default) |`CC-BY-SA-4.0` |Creative Commons Attribution Share Alike 4.0 | |(default) |`CC0-1.0` |Creative Commons Zero v1.0 Universal |`FSF GPL COPYFREE` |(default) |`CDDL` |Common Development and Distribution License |`FSF OSI` |(default) |`CPAL-1.0` |Common Public Attribution License |`FSF OSI` |(default) |`ClArtistic` |Clarified Artistic License |`FSF GPL OSI` |(default) |`EPL` |Eclipse Public License |`FSF OSI` |(default) |`GFDL` |GNU Free Documentation License |`FSF` |(default) |`GMGPL` |GNAT Modified General Public License |`FSF GPL OSI` |(default) |`GPLv1` |GNU General Public License version 1 |`FSF GPL OSI` |(default) |`GPLv1+` |GNU General Public License version 1 (or later) |`FSF GPL OSI` |(default) |`GPLv2` |GNU General Public License version 2 |`FSF GPL OSI` |(default) |`GPLv2+` |GNU General Public License version 2 (or later) |`FSF GPL OSI` |(default) |`GPLv3` |GNU General Public License version 3 |`FSF GPL OSI` |(default) |`GPLv3+` |GNU General Public License version 3 (or later) |`FSF GPL OSI` |(default) |`GPLv3RLE` |GNU GPL version 3 Runtime Library Exception |`FSF GPL OSI` |(default) |`GPLv3RLE+` |GNU GPL version 3 Runtime Library Exception (or later) |`FSF GPL OSI` |(default) |`ISCL` |Internet Systems Consortium License |`FSF GPL OSI COPYFREE` |(default) |`LGPL20` |GNU Library General Public License version 2.0 |`FSF GPL OSI` |(default) |`LGPL20+` |GNU Library General Public License version 2.0 (or later) |`FSF GPL OSI` |(default) |`LGPL21` |GNU Lesser General Public License version 2.1 |`FSF GPL OSI` |(default) |`LGPL21+` |GNU Lesser General Public License version 2.1 (or later) |`FSF GPL OSI` |(default) |`LGPL3` |GNU Lesser General Public License version 3 |`FSF GPL OSI` |(default) |`LGPL3+` |GNU Lesser General Public License version 3 (or later) |`FSF GPL OSI` |(default) |`LPPL10` |LaTeX Project Public License version 1.0 |`FSF OSI` |`dist-mirror dist-sell` |`LPPL11` |LaTeX Project Public License version 1.1 |`FSF OSI` |`dist-mirror dist-sell` |`LPPL12` |LaTeX Project Public License version 1.2 |`FSF OSI` |`dist-mirror dist-sell` |`LPPL13` |LaTeX Project Public License version 1.3 |`FSF OSI` |`dist-mirror dist-sell` |`LPPL13a` |LaTeX Project Public License version 1.3a |`FSF OSI` |`dist-mirror dist-sell` |`LPPL13b` |LaTeX Project Public License version 1.3b |`FSF OSI` |`dist-mirror dist-sell` |`LPPL13c` |LaTeX Project Public License version 1.3c |`FSF OSI` |`dist-mirror dist-sell` |`MIT` |MIT license / X11 license |`COPYFREE FSF GPL OSI` |(default) |`MPL10` |Mozilla Public License version 1.0 |`FSF OSI` |(default) |`MPL11` |Mozilla Public License version 1.1 |`FSF OSI` |(default) |`MPL20` |Mozilla Public License version 2.0 |`FSF OSI` |(default) |`NCSA` |University of Illinois/NCSA Open Source License |`COPYFREE FSF GPL OSI` |(default) |`NONE` |No license specified | |`none` |`OFL10` |SIL Open Font License version 1.0 (http://scripts.sil.org/OFL) |`FONTS` |(default) |`OFL11` |SIL Open Font License version 1.1 (http://scripts.sil.org/OFL) |`FONTS` |(default) |`OWL` |Open Works License (owl.apotheon.org) |`COPYFREE` |(default) |`OpenSSL` |OpenSSL License |`FSF` |(default) |`PD` |Public Domain |`GPL COPYFREE` |(default) |`PHP202` |PHP License version 2.02 |`FSF OSI` |(default) |`PHP30` |PHP License version 3.0 |`FSF OSI` |(default) |`PHP301` |PHP License version 3.01 |`FSF OSI` |(default) |`PSFL` |Python Software Foundation License |`FSF GPL OSI` |(default) |`PostgreSQL` |PostgreSQL License |`FSF GPL OSI COPYFREE` |(default) |`RUBY` |Ruby License |`FSF` |(default) |`UNLICENSE` |The Unlicense |`COPYFREE FSF GPL` |(default) |`WTFPL` |Do What the Fuck You Want To Public License version 2 |`GPL FSF COPYFREE` |(default) |`WTFPL1` |Do What the Fuck You Want To Public License version 1 |`GPL FSF COPYFREE` |(default) |`ZLIB` |zlib License |`GPL FSF OSI` |(default) |`ZPL21` |Zope Public License version 2.1 |`GPL OSI` |(default) |=== [[licenses-license_perms]] === `LICENSE_PERMS` and `LICENSE_PERMS_NAME_` Permissions. use `none` if empty. .License Permissions List [[licenses-license_perms-dist-mirror]] `dist-mirror`:: Redistribution of the distribution files is permitted. The distribution files will be added to the FreeBSD `MASTER_SITE_BACKUP` CDN. [[licenses-license_perms-no-dist-mirror]] `no-dist-mirror`:: Redistribution of the distribution files is prohibited. This is equivalent to setting crossref:special[porting-restrictions-restricted,`RESTRICTED`]. The distribution files will _not_ be added to the FreeBSD `MASTER_SITE_BACKUP` CDN. [[licenses-license_perms-dist-sell]] `dist-sell`:: Selling of distribution files is permitted. The distribution files will be present on the installer images. [[licenses-license_perms-no-dist-sell]] `no-dist-sell`:: Selling of distribution files is prohibited. This is equivalent to setting crossref:special[porting-restrictions-no_cdrom,`NO_CDROM`]. [[licenses-license_perms-pkg-mirror]] `pkg-mirror`:: Free redistribution of package is permitted. The package will be distributed on the FreeBSD package CDN https://pkg.freebsd.org/[https://pkg.freebsd.org/]. [[licenses-license_perms-no-pkg-mirror]] `no-pkg-mirror`:: Free redistribution of package is prohibited. Equivalent to setting crossref:special[porting-restrictions-no_package,`NO_PACKAGE`]. The package will _not_ be distributed from the FreeBSD package CDN https://pkg.freebsd.org/[https://pkg.freebsd.org/]. [[licenses-license_perms-pkg-sell]] `pkg-sell`:: Selling of package is permitted. The package will be present on the installer images. [[licenses-license_perms-no-pkg-sell]] `no-pkg-sell`:: Selling of package is prohibited. This is equivalent to setting crossref:special[porting-restrictions-no_cdrom,`NO_CDROM`]. The package will _not_ be present on the installer images. [[licenses-license_perms-auto-accept]] `auto-accept`:: License is accepted by default. Prompts to accept a license are not displayed unless the user has defined `LICENSES_ASK`. Use this unless the license states the user must accept the terms of the license. [[licenses-license_perms-no-auto-accept]] `no-auto-accept`:: License is not accepted by default. The user will always be asked to confirm the acceptance of this license. This must be used if the license states that the user must accept its terms. When both `_permission_` and `no-_permission_` is present the `no-_permission_` will cancel `_permission_`. When `_permission_` is not present, it is considered to be a `no-_permission_`. [WARNING] ==== Some missing permissions will prevent a port (and all ports depending on it) from being usable by package users: A port without the `auto-accept` permission will never be be built and all the ports depending on it will be ignored. A port without the `pkg-mirror` permission will be removed, as well as all the ports depending on it, after the build and they will ever end up being distributed. ==== [[licenses-license_perms-ex1]] .Nonstandard License [example] ==== Read the terms of the license and translate those using the available permissions. [.programlisting] .... LICENSE= UNKNOWN LICENSE_NAME= unknown LICENSE_TEXT= This program is NOT in public domain.\ It can be freely distributed for non-commercial purposes only. LICENSE_PERMS= dist-mirror no-dist-sell pkg-mirror no-pkg-sell auto-accept .... ==== [[licenses-license_perms-ex2]] .Standard and Nonstandard Licenses [example] ==== Read the terms of the license and express those using the available permissions. In case of doubt, please ask for guidance on the {freebsd-ports}. [.programlisting] .... LICENSE= WARSOW GPLv2 LICENSE_COMB= multi LICENSE_NAME_WARSOW= Warsow Content License LICENSE_FILE_WARSOW= ${WRKSRC}/docs/license.txt LICENSE_PERMS_WARSOW= dist-mirror pkg-mirror auto-accept .... When the permissions of the GPLv2 and the UNKNOWN licenses are mixed, the port ends up with `dist-mirror dist-sell pkg-mirror pkg-sell auto-accept dist-mirror no-dist-sell pkg-mirror no-pkg-sell auto-accept`. The `no-_permissions_` cancel the _permissions_. The resulting list of permissions are _dist-mirror pkg-mirror auto-accept_. The distribution files and the packages will not be available on the installer images. ==== [[licenses-license_groups]] === `LICENSE_GROUPS` and `LICENSE_GROUPS_NAME` Groups the license belongs. .Predefined License Groups List [[licenses-license_groups-FSF]] `FSF`:: Free Software Foundation Approved, see the http://www.fsf.org/licensing[FSF Licensing & Compliance Team]. [[licenses-license_groups-GPL]] `GPL`:: GPL Compatible [[licenses-license_groups-OSI]] `OSI`:: OSI Approved, see the Open Source Initiative http://opensource.org/licenses[Open Source Licenses] page. [[licenses-license_groups-COPYFREE]] `COPYFREE`:: Comply with Copyfree Standard Definition, see the http://copyfree.org/standard/licenses[Copyfree Licenses] page. [[licenses-license_groups-FONTS]] `FONTS`:: Font licenses [[licenses-license_name]] === `LICENSE_NAME` and `LICENSE_NAME_NAME` Full name of the license. [[licenses-license_name-ex1]] .`LICENSE_NAME` [example] ==== [.programlisting] .... LICENSE= UNRAR LICENSE_NAME= UnRAR License LICENSE_FILE= ${WRKSRC}/license.txt LICENSE_PERMS= dist-mirror dist-sell pkg-mirror pkg-sell auto-accept .... ==== [[licenses-license_file]] === `LICENSE_FILE` and `LICENSE_FILE_NAME` Full path to the file containing the license text, usually [.filename]#${WRKSRC}/some/file#. If the file is not in the distfile, and its content is too long to be put in <>, put it in a new file in [.filename]#${FILESDIR}#. [[licenses-license_file-ex1]] .`LICENSE_FILE` [example] ==== [.programlisting] .... LICENSE= GPLv3+ LICENSE_FILE= ${WRKSRC}/COPYING .... ==== [[licenses-license_text]] === `LICENSE_TEXT` and `LICENSE_TEXT_NAME` Text to use as a license. Useful when the license is not in the distribution files and its text is short. [[licenses-license_text-ex1]] .`LICENSE_TEXT` [example] ==== [.programlisting] .... LICENSE= UNKNOWN LICENSE_NAME= unknown LICENSE_TEXT= This program is NOT in public domain.\ It can be freely distributed for non-commercial purposes only,\ and THERE IS NO WARRANTY FOR THIS PROGRAM. LICENSE_PERMS= dist-mirror no-dist-sell pkg-mirror no-pkg-sell auto-accept .... ==== [[licenses-license_distfiles]] === `LICENSE_DISTFILES` and `LICENSE_DISTFILES_NAME` The distribution files to which the licenses apply. Defaults to all the distribution files. [[licenses-license_distfiles-ex1]] .`LICENSE_DISTFILES` [example] ==== Used when the distribution files do not all have the same license. For example, one has a code license, and another has some artwork that cannot be redistributed: [.programlisting] .... MASTER_SITES= SF/some-game DISTFILES= ${DISTNAME}${EXTRACT_SUFX} artwork.zip LICENSE= BSD3CLAUSE ARTWORK LICENSE_COMB= dual LICENSE_NAME_ARTWORK= The game artwork license LICENSE_TEXT_ARTWORK= The README says that the files cannot be redistributed LICENSE_PERMS_ARTWORK= pkg-mirror pkg-sell auto-accept LICENSE_DISTFILES_BSD3CLAUSE= ${DISTNAME}${EXTRACT_SUFX} LICENSE_DISTFILES_ARTWORK= artwork.zip .... ==== [[licenses-license_comb]] === `LICENSE_COMB` Set to `multi` if all licenses apply. Set to `dual` if any license applies. Defaults to `single`. [[licenses-license_comb-ex1]] .Dual Licenses [example] ==== When a port says "This software may be distributed under the GNU General Public License or the Artistic License", it means that either license can be used. Use this: [.programlisting] .... LICENSE= ART10 GPLv1 LICENSE_COMB= dual .... If license files are provided, use this: [.programlisting] .... LICENSE= ART10 GPLv1 LICENSE_COMB= dual LICENSE_FILE_ART10= ${WRKSRC}/Artistic LICENSE_FILE_GPLv1= ${WRKSRC}/Copying .... ==== [[licenses-license_comb-ex2]] .Multiple Licenses [example] ==== When part of a port has one license, and another part has a different license, use `multi`: [.programlisting] .... LICENSE= GPLv2 LGPL21+ LICENSE_COMB= multi .... ==== [[makefile-portscout]] == `PORTSCOUT` Portscout is an automated distfile check utility for the FreeBSD Ports Collection, described in detail in crossref:keeping-up[distfile-survey,Portscout: the FreeBSD Ports Distfile Scanner]. `PORTSCOUT` defines special conditions within which the Portscout distfile scanner is restricted. Situations where `PORTSCOUT` is set include: * When distfiles have to be ignored for specific versions. For example, to exclude version _8.2_ and version _8.3_ from distfile version checks because they are known to be broken, add: + [.programlisting] .... PORTSCOUT= skipv:8.2,8.3 .... * When distfile version checks have to be disabled completely. For example, if a port is not going to be updated ever again, add: + [.programlisting] .... PORTSCOUT= ignore:1 .... * When specific versions or specific major and minor revisions of a distfile must be checked. For example, if only version _0.6.4_ must be monitored because newer versions have compatibility issues with FreeBSD, add: + [.programlisting] .... PORTSCOUT= limit:^0\.6\.4 .... * When URLs listing the available versions differ from the download URLs. For example, to limit distfile version checks to the download page for the package:databases/pgtune[] port, add: + [.programlisting] .... PORTSCOUT= site:http://pgfoundry.org/frs/?group_id=1000416 .... [[makefile-depend]] == Dependencies Many ports depend on other ports. This is a very convenient feature of most Unix-like operating systems, including FreeBSD. Multiple ports can share a common dependency, rather than bundling that dependency with every port or package that needs it. There are seven variables that can be used to ensure that all the required bits will be on the user's machine. There are also some pre-supported dependency variables for common cases, plus a few more to control the behavior of dependencies. [IMPORTANT] ==== When software has extra dependencies that provide extra features, the base dependencies listed in `*_DEPENDS` should include the extra dependencies that would benefit most users. The base dependencies should never be a "minimal" dependency set. The goal is not to include every dependency possible. Only include those that will benefit most people. ==== [[makefile-lib_depends]] === `LIB_DEPENDS` This variable specifies the shared libraries this port depends on. It is a list of `_lib:dir_` tuples where `_lib_` is the name of the shared library, `_dir_` is the directory in which to find it in case it is not available. For example, [.programlisting] .... LIB_DEPENDS= libjpeg.so:graphics/jpeg .... will check for a shared jpeg library with any version, and descend into the [.filename]#graphics/jpeg# subdirectory of the ports tree to build and install it if it is not found. The dependency is checked twice, once from within the `build` target and then from within the `install` target. Also, the name of the dependency is put into the package so that `pkg install` (see man:pkg-install[8]) will automatically install it if it is not on the user's system. [[makefile-run_depends]] === `RUN_DEPENDS` This variable specifies executables or files this port depends on during run-time. It is a list of ``_path:dir_``[:``_target_``] tuples where `_path_` is the name of the executable or file, _dir_ is the directory in which to find it in case it is not available, and _target_ is the target to call in that directory. If _path_ starts with a slash (`/`), it is treated as a file and its existence is tested with `test -e`; otherwise, it is assumed to be an executable, and `which -s` is used to determine if the program exists in the search path. For example, [.programlisting] .... RUN_DEPENDS= ${LOCALBASE}/news/bin/innd:news/inn \ xmlcatmgr:textproc/xmlcatmgr .... will check if the file or directory [.filename]#/usr/local/news/bin/innd# exists, and build and install it from the [.filename]#news/inn# subdirectory of the ports tree if it is not found. It will also see if an executable called `xmlcatmgr` is in the search path, and descend into [.filename]#textproc/xmlcatmgr# to build and install it if it is not found. [NOTE] ==== In this case, `innd` is actually an executable; if an executable is in a place that is not expected to be in the search path, use the full pathname. ==== [NOTE] ==== The official search `PATH` used on the ports build cluster is [.programlisting] .... /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin .... ==== The dependency is checked from within the `install` target. Also, the name of the dependency is put into the package so that `pkg install` (see man:pkg-install[8]) will automatically install it if it is not on the user's system. The _target_ part can be omitted if it is the same as `DEPENDS_TARGET`. A quite common situation is when `RUN_DEPENDS` is literally the same as `BUILD_DEPENDS`, especially if ported software is written in a scripted language or if it requires the same build and run-time environment. In this case, it is both tempting and intuitive to directly assign one to the other: [.programlisting] .... RUN_DEPENDS= ${BUILD_DEPENDS} .... However, such assignment can pollute run-time dependencies with entries not defined in the port's original `BUILD_DEPENDS`. This happens because of man:make[1]'s lazy evaluation of variable assignment. Consider a [.filename]#Makefile# with `USE_*`, which are processed by [.filename]#ports/Mk/bsd.*.mk# to augment initial build dependencies. For example, `USES= gmake` adds package:devel/gmake[] to `BUILD_DEPENDS`. To prevent such additional dependencies from polluting `RUN_DEPENDS`, create another variable with the current content of `BUILD_DEPENDS` and assign it to both `BUILD_DEPENDS` and `RUN_DEPENDS`: [.programlisting] .... MY_DEPENDS= some:devel/some \ other:lang/other BUILD_DEPENDS= ${MY_DEPENDS} RUN_DEPENDS= ${MY_DEPENDS} .... [IMPORTANT] ==== _Do not_ use `:=` to assign `BUILD_DEPENDS` to `RUN_DEPENDS` or vice-versa. All variables are expanded immediately, which is exactly the wrong thing to do and almost always a failure. ==== [[makefile-build_depends]] === `BUILD_DEPENDS` This variable specifies executables or files this port requires to build. Like `RUN_DEPENDS`, it is a list of ``_path:dir_``[:``_target_``] tuples. For example, [.programlisting] .... BUILD_DEPENDS= unzip:archivers/unzip .... will check for an executable called `unzip`, and descend into the [.filename]#archivers/unzip# subdirectory of the ports tree to build and install it if it is not found. [NOTE] ==== "build" here means everything from extraction to compilation. The dependency is checked from within the `extract` target. The _target_ part can be omitted if it is the same as `DEPENDS_TARGET` ==== [[makefile-fetch_depends]] === `FETCH_DEPENDS` This variable specifies executables or files this port requires to fetch. Like the previous two, it is a list of ``_path:dir_``[:``_target_``] tuples. For example, [.programlisting] .... FETCH_DEPENDS= ncftp2:net/ncftp2 .... will check for an executable called `ncftp2`, and descend into the [.filename]#net/ncftp2# subdirectory of the ports tree to build and install it if it is not found. The dependency is checked from within the `fetch` target. The _target_ part can be omitted if it is the same as `DEPENDS_TARGET`. [[makefile-extract_depends]] === `EXTRACT_DEPENDS` This variable specifies executables or files this port requires for extraction. Like the previous, it is a list of ``_path:dir_``[:``_target_``] tuples. For example, [.programlisting] .... EXTRACT_DEPENDS= unzip:archivers/unzip .... will check for an executable called `unzip`, and descend into the [.filename]#archivers/unzip# subdirectory of the ports tree to build and install it if it is not found. The dependency is checked from within the `extract` target. The _target_ part can be omitted if it is the same as `DEPENDS_TARGET`. [NOTE] ==== Use this variable only if the extraction does not already work (the default assumes `tar`) and cannot be made to work using `USES=tar`, `USES=lha` or `USES=zip` described in crossref:uses[uses,Using `USES` Macros]. ==== [[makefile-patch_depends]] === `PATCH_DEPENDS` This variable specifies executables or files this port requires to patch. Like the previous, it is a list of ``_path:dir_``[:``_target_``] tuples. For example, [.programlisting] .... PATCH_DEPENDS= ${NONEXISTENT}:java/jfc:extract .... will descend into the [.filename]#java/jfc# subdirectory of the ports tree to extract it. The dependency is checked from within the `patch` target. The _target_ part can be omitted if it is the same as `DEPENDS_TARGET`. [[makefile-uses]] === `USES` Parameters can be added to define different features and dependencies used by the port. They are specified by adding this line to the [.filename]#Makefile#: [.programlisting] .... USES= feature[:arguments] .... For the complete list of values, please see crossref:uses[uses,Using `USES` Macros]. [WARNING] ==== `USES` cannot be assigned after inclusion of [.filename]#bsd.port.pre.mk#. ==== [[makefile-use-vars]] === `USE_*` Several variables exist to define common dependencies shared by many ports. Their use is optional, but helps to reduce the verbosity of the port [.filename]##Makefile##s. Each of them is styled as `USE_*`. These variables may be used only in the port [.filename]##Makefile##s and [.filename]#ports/Mk/bsd.*.mk#. They are not meant for user-settable options - use `PORT_OPTIONS` for that purpose. [NOTE] ==== It is _always_ incorrect to set any `USE_*` in [.filename]#/etc/make.conf#. For instance, setting [.programlisting] .... USE_GCC=X.Y .... (where X.Y is version number) would add a dependency on gccXY for every port, including `lang/gccXY` itself! ==== [[makefile-use-vars-table]] .`USE_*` [cols="1,1", frame="none", options="header"] |=== | Variable | Means |`USE_GCC` a| The port requires GCC (`gcc` or `{g-plus-plus}`) to build. Some ports need a specific, old GCC version, some require modern, recent versions. It is typically set to `yes` (means always use stable, modern GCC from ports per `GCC_DEFAULT` in [.filename]#Mk/bsd.default-versions.mk#). This is also the default value. The exact version can also be specified, with a value such as `10`. The minimal required version can be specified as `10+`. GCC from the base system is used when it satisfies the requested version, otherwise an appropriate compiler is built from ports, and `CC` and `CXX` are adjusted accordingly. The `:build` argument following the version specifier adds only a build time dependency to the port. For example: [example] ==== [.programlisting] .... USE_GCC=yes # port requires a current version of GCC USE_GCC=11+:build # port requires GCC 11 or later at build time only .... ==== [NOTE] ==== `USE_GCC=any` is deprecated and should not be used in new ports ==== |=== Variables related to gmake and [.filename]#configure# are described in crossref:special[building,Building Mechanisms], while autoconf, automake and libtool are described in crossref:special[using-autotools,Using GNU Autotools]. Perl related variables are described in crossref:special[using-perl,Using Perl]. X11 variables are listed in crossref:special[using-x11,Using X11]. crossref:special[using-gnome,Using Gnome] deals with GNOME and crossref:special[using-kde,Using KDE] with KDE related variables. crossref:special[using-java,Using Java] documents Java variables, while crossref:special[using-php,Web Applications, Apache and PHP] contains information on Apache, PHP and PEAR modules. Python is discussed in crossref:special[using-python,Using Python], while Ruby in crossref:special[using-ruby,Using Ruby]. crossref:special[using-sdl,Using SDL] provides variables used for SDL applications and finally, crossref:special[using-xfce,Using Xfce] contains information on Xfce. [[makefile-version-dependency]] === Minimal Version of a Dependency A minimal version of a dependency can be specified in any `*_DEPENDS` except `LIB_DEPENDS` using this syntax: [.programlisting] .... p5-Spiffy>=0.26:devel/p5-Spiffy .... The first field contains a dependent package name, which must match the entry in the package database, a comparison sign, and a package version. The dependency is satisfied if p5-Spiffy-0.26 or newer is installed on the machine. [[makefile-note-on-dependencies]] === Notes on Dependencies As mentioned above, the default target to call when a dependency is required is `DEPENDS_TARGET`. It defaults to `install`. This is a user variable; it is never defined in a port's [.filename]#Makefile#. If the port needs a special way to handle a dependency, use the `:target` part of `*_DEPENDS` instead of redefining `DEPENDS_TARGET`. When running `make clean`, the port dependencies are automatically cleaned too. If this is not desirable, define `NOCLEANDEPENDS` in the environment. This may be particularly desirable if the port has something that takes a long time to rebuild in its dependency list, such as KDE, GNOME or Mozilla. To depend on another port unconditionally, use the variable `${NONEXISTENT}` as the first field of `BUILD_DEPENDS` or `RUN_DEPENDS`. Use this only when the source of the other port is needed. Compilation time can be saved by specifying the target too. For instance [.programlisting] .... BUILD_DEPENDS= ${NONEXISTENT}:graphics/jpeg:extract .... will always descend to the `jpeg` port and extract it. [[makefile-circular-dependencies]] === Circular Dependencies Are Fatal [IMPORTANT] ==== Do not introduce any circular dependencies into the ports tree! ==== The ports building technology does not tolerate circular dependencies. If one is introduced, someone, somewhere in the world, will have their FreeBSD installation broken almost immediately, with many others quickly to follow. These can really be hard to detect. If in doubt, before making that change, make sure to run: `cd /usr/ports; make index`. That process can be quite slow on older machines, but it may be able to save a large number of people, including yourself, a lot of grief in the process. [[makefile-automatic-dependencies]] === Problems Caused by Automatic Dependencies Dependencies must be declared either explicitly or by using the <>. Using other methods like automatic detection complicates indexing, which causes problems for port and package management. [[makefile-automatic-dependencies-bad]] .Wrong Declaration of an Optional Dependency [example] ==== [.programlisting] .... .include .if exists(${LOCALBASE}/bin/foo) LIB_DEPENDS= libbar.so:foo/bar .endif .... ==== The problem with trying to automatically add dependencies is that files and settings outside an individual port can change at any time. For example: an index is built, then a batch of ports are installed. But one of the ports installs the tested file. The index is now incorrect, because an installed port unexpectedly has a new dependency. The index may still be wrong even after rebuilding if other ports also determine their need for dependencies based on the existence of other files. [[makefile-automatic-dependencies-good]] .Correct Declaration of an Optional Dependency [example] ==== [.programlisting] .... OPTIONS_DEFINE= BAR BAR_DESC= Calling cellphones via bar BAR_LIB_DEPENDS= libbar.so:foo/bar .... ==== Testing option variables is the correct method. It will not cause inconsistencies in the index of a batch of ports, provided the options were defined prior to the index build. Simple scripts can then be used to automate the building, installation, and updating of these ports and their packages. [[makefile-masterdir]] == Slave Ports and `MASTERDIR` If the port needs to build slightly different versions of packages by having a variable (for instance, resolution, or paper size) take different values, create one subdirectory per package to make it easier for users to see what to do, but try to share as many files as possible between ports. Typically, by using variables cleverly, only a very short [.filename]#Makefile# is needed in all but one of the directories. In the sole [.filename]#Makefile#, use `MASTERDIR` to specify the directory where the rest of the files are. Also, use a variable as part of <> so the packages will have different names. This will be best demonstrated by an example. This is part of [.filename]#print/pkfonts300/Makefile#; [.programlisting] .... PORTNAME= pkfonts${RESOLUTION} PORTVERSION= 1.0 DISTFILES= pk${RESOLUTION}.tar.gz PLIST= ${PKGDIR}/pkg-plist.${RESOLUTION} .if !defined(RESOLUTION) RESOLUTION= 300 .else .if ${RESOLUTION} != 118 && ${RESOLUTION} != 240 && \ ${RESOLUTION} != 300 && ${RESOLUTION} != 360 && \ ${RESOLUTION} != 400 && ${RESOLUTION} != 600 .BEGIN: @${ECHO_MSG} "Error: invalid value for RESOLUTION: \"${RESOLUTION}\"" @${ECHO_MSG} "Possible values are: 118, 240, 300, 360, 400 and 600." @${FALSE} .endif .endif .... package:print/pkfonts300[] also has all the regular patches, package files, etc. Running `make` there, it will take the default value for the resolution (300) and build the port normally. As for other resolutions, this is the _entire_ [.filename]#print/pkfonts360/Makefile#: [.programlisting] .... RESOLUTION= 360 MASTERDIR= ${.CURDIR}/../pkfonts300 .include "${MASTERDIR}/Makefile" .... ([.filename]#print/pkfonts118/Makefile#, [.filename]#print/pkfonts600/Makefile#, and all the other are similar). `MASTERDIR` definition tells [.filename]#bsd.port.mk# that the regular set of subdirectories like `FILESDIR` and `SCRIPTDIR` are to be found under [.filename]#pkfonts300#. The `RESOLUTION=360` line will override the `RESOLUTION=300` line in [.filename]#pkfonts300/Makefile# and the port will be built with resolution set to 360. [[makefile-manpages]] == Man Pages If the port anchors its man tree somewhere other than `PREFIX`, use `MANDIRS` to specify those directories. Note that the files corresponding to manual pages must be placed in [.filename]#pkg-plist# along with the rest of the files. The purpose of `MANDIRS` is to enable automatic compression of manual pages, therefore the file names are suffixed with [.filename]#.gz#. [[makefile-info]] == Info Files If the package needs to install GNU info files, list them in `INFO` (without the trailing `.info`), one entry per document. These files are assumed to be installed to [.filename]#PREFIX/INFO_PATH#. Change `INFO_PATH` if the package uses a different location. However, this is not recommended. These entries contain just the path relative to [.filename]#PREFIX/INFO_PATH#. For example, package:lang/gcc34[] installs info files to [.filename]#PREFIX/INFO_PATH/gcc34#, and `INFO` will be something like this: [.programlisting] .... INFO= gcc34/cpp gcc34/cppinternals gcc34/g77 ... .... Appropriate installation/de-installation code will be automatically added to the temporary [.filename]#pkg-plist# before package registration. [[makefile-options]] == Makefile Options Many applications can be built with optional or differing configurations. Examples include choice of natural (human) language, GUI versus command-line, or type of database to support. Users may need a different configuration than the default, so the ports system provides hooks the port author can use to control which variant will be built. Supporting these options properly will make users happy, and effectively provide two or more ports for the price of one. [[makefile-options-options]] === `OPTIONS` [[makefile-options-background]] ==== Background `OPTIONS_*` give the user installing the port a dialog showing the available options, and then saves those options to [.filename]#${PORT_DBDIR}/${OPTIONS_NAME}/options#. The next time the port is built, the options are reused. `PORT_DBDIR` defaults to [.filename]#/var/db/ports#. `OPTIONS_NAME` is to the port origin with an underscore as the space separator, for example, for package:dns/bind99[] it will be `dns_bind99`. When the user runs `make config` (or runs `make build` for the first time), the framework checks for [.filename]#${PORT_DBDIR}/${OPTIONS_NAME}/options#. If that file does not exist, the values of `OPTIONS_*` are used, and a dialog box is displayed where the options can be enabled or disabled. Then [.filename]#options# is saved and the configured variables are used when building the port. If a new version of the port adds new `OPTIONS`, the dialog will be presented to the user with the saved values of old `OPTIONS` prefilled. `make showconfig` shows the saved configuration. Use `make rmconfig` to remove the saved configuration. [[makefile-options-syntax]] ==== Syntax `OPTIONS_DEFINE` contains a list of `OPTIONS` to be used. These are independent of each other and are not grouped: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 .... Once defined, `OPTIONS` are described (optional, but strongly recommended): [.programlisting] .... OPT1_DESC= Describe OPT1 OPT2_DESC= Describe OPT2 OPT3_DESC= Describe OPT3 OPT4_DESC= Describe OPT4 OPT5_DESC= Describe OPT5 OPT6_DESC= Describe OPT6 .... [.filename]#ports/Mk/bsd.options.desc.mk# has descriptions for many common `OPTIONS`. While often useful, override them if the description is insufficient for the port. [TIP] ==== When describing options, view it from the perspective of the user: "What functionality does it change?" and "Why would I want to enable this?" Do not just repeat the name. For example, describing the `NLS` option as "include NLS support" does not help the user, who can already see the option name but may not know what it means. Describing it as "Native Language Support via gettext utilities" is much more helpful. ==== [IMPORTANT] ==== Option names are always in all uppercase. They cannot use mixed case or lowercase. ==== `OPTIONS` can be grouped as radio choices, where only one choice from each group is allowed: [.programlisting] .... OPTIONS_SINGLE= SG1 OPTIONS_SINGLE_SG1= OPT3 OPT4 .... [WARNING] ==== There _must_ be one of each `OPTIONS_SINGLE` group selected at all times for the options to be valid. One option of each group _must_ be added to `OPTIONS_DEFAULT`. ==== `OPTIONS` can be grouped as radio choices, where none or only one choice from each group is allowed: [.programlisting] .... OPTIONS_RADIO= RG1 OPTIONS_RADIO_RG1= OPT7 OPT8 .... `OPTIONS` can also be grouped as "multiple-choice" lists, where _at least one_ option must be enabled: [.programlisting] .... OPTIONS_MULTI= MG1 OPTIONS_MULTI_MG1= OPT5 OPT6 .... `OPTIONS` can also be grouped as "multiple-choice" lists, where none or any option can be enabled: [.programlisting] .... OPTIONS_GROUP= GG1 OPTIONS_GROUP_GG1= OPT9 OPT10 .... `OPTIONS` are unset by default, unless they are listed in `OPTIONS_DEFAULT`: [.programlisting] .... OPTIONS_DEFAULT= OPT1 OPT3 OPT6 .... `OPTIONS` definitions must appear before the inclusion of [.filename]#bsd.port.options.mk#. `PORT_OPTIONS` values can only be tested after the inclusion of [.filename]#bsd.port.options.mk#. Inclusion of [.filename]#bsd.port.pre.mk# can be used instead, too, and is still widely used in ports written before the introduction of [.filename]#bsd.port.options.mk#. But be aware that some variables will not work as expected after the inclusion of [.filename]#bsd.port.pre.mk#, typically some `USE_*` flags. [[ports-options-simple-use]] .Simple Use of `OPTIONS` [example] ==== [.programlisting] .... OPTIONS_DEFINE= FOO BAR OPTIONS_DEFAULT=FOO FOO_DESC= Option foo support BAR_DESC= Feature bar support # Will add --with-foo / --without-foo FOO_CONFIGURE_WITH= foo BAR_RUN_DEPENDS= bar:bar/bar .include .... ==== [[ports-options-check-unset]] .Check for Unset Port `OPTIONS` [example] ==== [.programlisting] .... .if ! ${PORT_OPTIONS:MEXAMPLES} CONFIGURE_ARGS+=--without-examples .endif .... The form shown above is discouraged. The preferred method is using a configure knob to really enable and disable the feature to match the option: [.programlisting] .... # Will add --with-examples / --without-examples EXAMPLES_CONFIGURE_WITH= examples .... ==== [[ports-options-practical-use]] .Practical Use of `OPTIONS` [example] ==== [.programlisting] .... OPTIONS_DEFINE= EXAMPLES OPTIONS_DEFAULT= PGSQL LDAP SSL OPTIONS_SINGLE= BACKEND OPTIONS_SINGLE_BACKEND= MYSQL PGSQL BDB OPTIONS_MULTI= AUTH OPTIONS_MULTI_AUTH= LDAP PAM SSL EXAMPLES_DESC= Install extra examples MYSQL_DESC= Use MySQL as backend PGSQL_DESC= Use PostgreSQL as backend BDB_DESC= Use Berkeley DB as backend LDAP_DESC= Build with LDAP authentication support PAM_DESC= Build with PAM support SSL_DESC= Build with OpenSSL support # Will add USE_PGSQL=yes PGSQL_USE= pgsql=yes # Will add --enable-postgres / --disable-postgres PGSQL_CONFIGURE_ENABLE= postgres ICU_LIB_DEPENDS= libicuuc.so:devel/icu # Will add --with-examples / --without-examples EXAMPLES_CONFIGURE_WITH= examples # Check other OPTIONS .include .... ==== [[makefile-options-default]] ==== Default Options These options are always on by default. * `DOCS` - build and install documentation. * `NLS` - Native Language Support. * `EXAMPLES` - build and install examples. * `IPV6` - IPv6 protocol support. [NOTE] ==== There is no need to add these to `OPTIONS_DEFAULT`. To have them active, and show up in the options selection dialog, however, they must be added to `OPTIONS_DEFINE`. ==== [[makefile-options-auto-activation]] === Feature Auto-Activation When using a GNU configure script, keep an eye on which optional features are activated by auto-detection. Explicitly disable optional features that are not needed by adding `--without-xxx` or `--disable-xxx` in `CONFIGURE_ARGS`. [[makefile-options-auto-activation-bad]] .Wrong Handling of an Option [example] ==== [.programlisting] .... .if ${PORT_OPTIONS:MFOO} LIB_DEPENDS+= libfoo.so:devel/foo CONFIGURE_ARGS+= --enable-foo .endif .... ==== In the example above, imagine a library libfoo is installed on the system. The user does not want this application to use libfoo, so he toggled the option off in the `make config` dialog. But the application's configure script detects the library present in the system and includes its support in the resulting executable. Now when the user decides to remove libfoo from the system, the ports system does not protest (no dependency on libfoo was recorded) but the application breaks. [[makefile-options-auto-activation-good]] .Correct Handling of an Option [example] ==== [.programlisting] .... FOO_LIB_DEPENDS= libfoo.so:devel/foo # Will add --enable-foo / --disable-foo FOO_CONFIGURE_ENABLE= foo .... ==== [NOTE] ==== Under some circumstances, the shorthand conditional syntax can cause problems with complex constructs. The errors are usually `Malformed conditional`, an alternative syntax can be used. [.programlisting] .... .if !empty(VARIABLE:MVALUE) .... as an alternative to [.programlisting] .... .if ${VARIABLE:MVALUE} .... ==== [[options-helpers]] === Options Helpers There are some macros to help simplify conditional values which differ based on the options set. For easier access, a comprehensive list is provided: `PLIST_SUB`, `SUB_LIST`:: For automatic `%%_OPT_%%` and `%%NO__OPT__%%` generation, see <>. + For more complex usage, see <>. `CONFIGURE_ARGS`:: For `--enable-_x_` and `--disable-_x_`, see <>. + For `--with-_x_` and `--without-_x_`, see <>. + For all other cases, see <>. `CMAKE_ARGS`:: For arguments that are booleans (`on`, `off`, `true`, `false`, `0`, `1`) see <>. + For all other cases, see <>. `MESON_ARGS`:: For arguments that take `true` or `false`, see <>. + For arguments that take `yes` or `no`, use <>. + For arguments that take `enabled` or `disabled`, see <>. + For all other cases, use <>. `QMAKE_ARGS`:: See <>. `USE_*`:: See <>. `*_DEPENDS`:: See <>. `*` (Any variable):: The most used variables have direct helpers, see <>. + For any variable without a specific helper, see <>. Options dependencies:: When an option need another option to work, see <>. Options conflicts:: When an option cannot work if another is also enabled, see <>. Build targets:: When an option need some extra processing, see <>. [[options_sub]] ==== `OPTIONS_SUB` If `OPTIONS_SUB` is set to `yes` then each of the options added to `OPTIONS_DEFINE` will be added to `PLIST_SUB` and `SUB_LIST`, for example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPTIONS_SUB= yes .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} PLIST_SUB+= OPT1="" NO_OPT1="@comment " SUB_LIST+= OPT1="" NO_OPT1="@comment " .else PLIST_SUB+= OPT1="@comment " NO_OPT1="" SUB_LIST+= OPT1="@comment " NO_OPT1="" .endif .... [NOTE] ==== The value of `OPTIONS_SUB` is ignored. Setting it to any value will add `PLIST_SUB` and `SUB_LIST` entries for _all_ options. ==== [[options-use]] ==== `OPT_USE` and `OPT_USE_OFF` When option _OPT_ is selected, for each `_key=value_` pair in ``OPT_USE``, _value_ is appended to the corresponding `USE_KEY`. If _value_ has spaces in it, replace them with commas and they will be changed back to spaces during processing. `OPT_USE_OFF` works the same way, but when `OPT` is _not_ selected. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_USES= xorg OPT1_USE= mysql=yes xorg=x11,xextproto,xext,xrandr OPT1_USE_OFF= openssl=yes .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} USE_MYSQL= yes USES+= xorg USE_XORG= x11 xextproto xext xrandr .else USE_OPENSSL= yes .endif .... [[options-configure-helpers]] ==== `CONFIGURE_ARGS` Helpers [[options-configure_enable]] ===== `OPT_CONFIGURE_ENABLE` When option _OPT_ is selected, for each _entry_ in `OPT_CONFIGURE_ENABLE` then `--enable-_entry_` is appended to `CONFIGURE_ARGS`. When option _OPT_ is _not_ selected, `--disable-_entry_` is appended to `CONFIGURE_ARGS`. An optional argument can be specified with an `=` symbol. This argument is only appended to the `--enable-_entry_` configure option. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 OPT1_CONFIGURE_ENABLE= test1 test2 OPT2_CONFIGURE_ENABLE= test2=exhaustive .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} CONFIGURE_ARGS+= --enable-test1 --enable-test2 .else CONFIGURE_ARGS+= --disable-test1 --disable-test2 .endif .if ${PORT_OPTIONS:MOPT2} CONFIGURE_ARGS+= --enable-test2=exhaustive .else CONFIGURE_ARGS+= --disable-test2 .endif .... [[options-configure_with]] ===== `OPT_CONFIGURE_WITH` When option _OPT_ is selected, for each _entry_ in `_OPT_CONFIGURE_WITH` then `--with-_entry_` is appended to `CONFIGURE_ARGS`. When option _OPT_ is _not_ selected, `--without-_entry_` is appended to `CONFIGURE_ARGS`. An optional argument can be specified with an `=` symbol. This argument is only appended to the `--with-_entry_` configure option. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 OPT1_CONFIGURE_WITH= test1 OPT2_CONFIGURE_WITH= test2=exhaustive .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 .include .if ${PORT_OPTIONS:MOPT1} CONFIGURE_ARGS+= --with-test1 .else CONFIGURE_ARGS+= --without-test1 .endif .if ${PORT_OPTIONS:MOPT2} CONFIGURE_ARGS+= --with-test2=exhaustive .else CONFIGURE_ARGS+= --without-test2 .endif .... [[options-configure_on]] ===== `OPT_CONFIGURE_ON` and `OPT_CONFIGURE_OFF` When option _OPT_ is selected, the value of `OPT_CONFIGURE_ON`, if defined, is appended to `CONFIGURE_ARGS`. `OPT_CONFIGURE_OFF` works the same way, but when `OPT` is _not_ selected. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_CONFIGURE_ON= --add-test OPT1_CONFIGURE_OFF= --no-test .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} CONFIGURE_ARGS+= --add-test .else CONFIGURE_ARGS+= --no-test .endif .... [TIP] ==== Most of the time, the helpers in <> and <> provide a shorter and more comprehensive functionality. ==== [[options-cmake-helpers]] ==== `CMAKE_ARGS` Helpers [[options-cmake_on]] ===== `OPT_CMAKE_ON` and `OPT_CMAKE_OFF` When option _OPT_ is selected, the value of `OPT_CMAKE_ON`, if defined, is appended to `CMAKE_ARGS`. `OPT_CMAKE_OFF` works the same way, but when `OPT` is _not_ selected. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_CMAKE_ON= -DTEST:BOOL=true -DDEBUG:BOOL=true OPT1_CMAKE_OFF= -DOPTIMIZE:BOOL=true .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} CMAKE_ARGS+= -DTEST:BOOL=true -DDEBUG:BOOL=true .else CMAKE_ARGS+= -DOPTIMIZE:BOOL=true .endif .... [TIP] ==== See <> for a shorter helper when the value is boolean. ==== [[options-cmake_bool]] ===== `OPT_CMAKE_BOOL` and `OPT_CMAKE_BOOL_OFF` When option _OPT_ is selected, for each _entry_ in `OPT_CMAKE_BOOL` then `-D_entry_:BOOL=true` is appended to `CMAKE_ARGS`. When option _OPT_ is _not_ selected, `-D_entry_:BOOL=false` is appended to `CONFIGURE_ARGS`. `OPT_CMAKE_BOOL_OFF` is the opposite, `-D_entry_:BOOL=false` is appended to `CMAKE_ARGS` when the option is selected, and `-D_entry_:BOOL=true` when the option is _not_ selected. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_CMAKE_BOOL= TEST DEBUG OPT1_CMAKE_BOOL_OFF= OPTIMIZE .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} CMAKE_ARGS+= -DTEST:BOOL=true -DDEBUG:BOOL=true \ -DOPTIMIZE:BOOL=false .else CMAKE_ARGS+= -DTEST:BOOL=false -DDEBUG:BOOL=false \ -DOPTIMIZE:BOOL=true .endif .... [[options-meson-helpers]] ==== `MESON_ARGS` Helpers [[options-meson_on]] ===== `OPT_MESON_ON` and `OPT_MESON_OFF` When option _OPT_ is selected, the value of `OPT_MESON_ON`, if defined, is appended to `MESON_ARGS`. `OPT_MESON_OFF` works the same way, but when `OPT` is _not_ selected. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_MESON_ON= -Dopt=1 OPT1_MESON_OFF= -Dopt=2 .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} MESON_ARGS+= -Dopt=1 .else MESON_ARGS+= -Dopt=2 .endif .... [[options-meson_true]] ===== `OPT_MESON_TRUE` and `OPT_MESON_FALSE` When option _OPT_ is selected, for each _entry_ in `OPT_MESON_TRUE` then `-D_entry_=true` is appended to `MESON_ARGS`. When option _OPT_ is _not_ selected, `-D_entry_=false` is appended to `MESON_ARGS`. `OPT_MESON_FALSE` is the opposite, `-D_entry_=false` is appended to `MESON_ARGS` when the option is selected, and `-D_entry_=true` when the option is _not_ selected. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_MESON_TRUE= test debug OPT1_MESON_FALSE= optimize .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} MESON_ARGS+= -Dtest=true -Ddebug=true \ -Doptimize=false .else MESON_ARGS+= -Dtest=false -Ddebug=false \ -Doptimize=true .endif .... [[options-meson_yes]] ===== `OPT_MESON_YES` and `OPT_MESON_NO` When option _OPT_ is selected, for each _entry_ in `OPT_MESON_YES` then `-D_entry_=yes` is appended to `MESON_ARGS`. When option _OPT_ is _not_ selected, `-D_entry_=no` is appended to `MESON_ARGS`. `OPT_MESON_NO` is the opposite, `-D_entry_=no` is appended to `MESON_ARGS` when the option is selected, and `-D_entry_=yes` when the option is _not_ selected. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_MESON_YES= test debug OPT1_MESON_NO= optimize .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} MESON_ARGS+= -Dtest=yes -Ddebug=yes \ -Doptimize=no .else MESON_ARGS+= -Dtest=no -Ddebug=no \ -Doptimize=yes .endif .... [[options-meson_enabled]] ===== `OPT_MESON_ENABLED` and `OPT_MESON_DISABLED` When option _OPT_ is selected, for each _entry_ in `OPT_MESON_ENABLED` then `-D_entry_=enabled` is appended to `MESON_ARGS`. When option _OPT_ is _not_ selected, `-D_entry_=disabled` is appended to `MESON_ARGS`. `OPT_MESON_DISABLED` is the opposite, `-D_entry_=disabled` is appended to `MESON_ARGS` when the option is selected, and `-D_entry_=enabled` when the option is _not_ selected. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_MESON_ENABLED= test OPT1_MESON_DISABLED= debug .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} MESON_ARGS+= -Dtest=enabled -Ddebug=disabled .else MESON_ARGS+= -Dtest=disabled -Ddebug=enabled .endif .... [[options-qmake_on]] ==== `OPT_QMAKE_ON` and `OPT_QMAKE_OFF` When option _OPT_ is selected, the value of `OPT_QMAKE_ON`, if defined, is appended to `QMAKE_ARGS`. `OPT_QMAKE_OFF` works the same way, but when `OPT` is _not_ selected. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_QMAKE_ON= -DTEST:BOOL=true OPT1_QMAKE_OFF= -DPRODUCTION:BOOL=true .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} QMAKE_ARGS+= -DTEST:BOOL=true .else QMAKE_ARGS+= -DPRODUCTION:BOOL=true .endif .... [[options-implies]] ==== `OPT_IMPLIES` Provides a way to add dependencies between options. When _OPT_ is selected, all the options listed in this variable will be selected too. Using the <> described earlier to illustrate: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 OPT1_IMPLIES= OPT2 OPT1_CONFIGURE_ENABLE= opt1 OPT2_CONFIGURE_ENABLE= opt2 .... Is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 .include .if ${PORT_OPTIONS:MOPT1} CONFIGURE_ARGS+= --enable-opt1 .else CONFIGURE_ARGS+= --disable-opt1 .endif .if ${PORT_OPTIONS:MOPT2} || ${PORT_OPTIONS:MOPT1} CONFIGURE_ARGS+= --enable-opt2 .else CONFIGURE_ARGS+= --disable-opt2 .endif .... [[options-implies-ex1]] .Simple Use of `OPT_IMPLIES` [example] ==== This port has a `X11` option, and a `GNOME` option that needs the `X11` option to be selected to build. [.programlisting] .... OPTIONS_DEFINE= X11 GNOME OPTIONS_DEFAULT= X11 X11_USES= xorg X11_USE= xorg=xi,xextproto GNOME_USE= gnome=gtk30 GNOME_IMPLIES= X11 .... ==== [[options-prevents]] ==== `OPT_PREVENTS` and `OPT_PREVENTS_MSG` Provides a way to add conflicts between options. When _OPT_ is selected, all the options listed in `OPT_PREVENTS` must be un-selected. If `OPT_PREVENTS_MSG` is set and a conflict is triggered, its content will be shown explaining why they conflict. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 OPT1_PREVENTS= OPT2 OPT1_PREVENTS_MSG= OPT1 and OPT2 enable conflicting options .... Is roughly equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 .include .if ${PORT_OPTIONS:MOPT2} && ${PORT_OPTIONS:MOPT1} BROKEN= Option OPT1 conflicts with OPT2 (select only one) .endif .... The only difference is that the first one will write an error after running `make config`, suggesting changing the selected options. [[options-prevents-ex1]] .Simple Use of `OPT_PREVENTS` [example] ==== This port has `X509` and `SCTP` options. Both options add patches, but the patches conflict with each other, so they cannot be selected at the same time. [.programlisting] .... OPTIONS_DEFINE= X509 SCTP SCTP_PATCHFILES= ${PORTNAME}-6.8p1-sctp-2573.patch.gz:-p1 SCTP_CONFIGURE_WITH= sctp X509_PATCH_SITES= http://www.roumenpetrov.info/openssh/x509/:x509 X509_PATCHFILES= ${PORTNAME}-7.0p1+x509-8.5.diff.gz:-p1:x509 X509_PREVENTS= SCTP X509_PREVENTS_MSG= X509 and SCTP patches conflict .... ==== [[options-vars]] ==== `OPT_VARS` and `OPT_VARS_OFF` Provides a generic way to set and append to variables. [WARNING] ==== Before using `OPT_VARS` and `OPT_VARS_OFF`, see if there is already a more specific helper available in <>. ==== When option _OPT_ is selected, and `OPT_VARS` defined, `_key_=_value_` and `_key_+=_value_` pairs are evaluated from `OPT_VARS`. An `=` cause the existing value of `KEY` to be overwritten, an `+=` appends to the value. `OPT_VARS_OFF` works the same way, but when `OPT` is _not_ selected. [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 OPT3 OPT1_VARS= also_build+=bin1 OPT2_VARS= also_build+=bin2 OPT3_VARS= bin3_build=yes OPT3_VARS_OFF= bin3_build=no MAKE_ARGS= ALSO_BUILD="${ALSO_BUILD}" BIN3_BUILD="${BIN3_BUILD}" .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT2 MAKE_ARGS= ALSO_BUILD="${ALSO_BUILD}" BIN3_BUILD="${BIN3_BUILD}" .include .if ${PORT_OPTIONS:MOPT1} ALSO_BUILD+= bin1 .endif .if ${PORT_OPTIONS:MOPT2} ALSO_BUILD+= bin2 .endif .if ${PORT_OPTIONS:MOPT2} BIN3_BUILD= yes .else BIN3_BUILD= no .endif .... [IMPORTANT] ==== Values containing whitespace must be enclosed in quotes: [.programlisting] .... OPT_VARS= foo="bar baz" .... This is due to the way man:make[1] variable expansion deals with whitespace. When `OPT_VARS= foo=bar baz` is expanded, the variable ends up containing two strings, `foo=bar` and `baz`. But the submitter probably intended there to be only one string, `foo=bar baz`. Quoting the value prevents whitespace from being used as a delimiter. Also, _do not_ add extra spaces after the `_var_=` sign and before the value, it would also be split into two strings. _This will not work_: [.programlisting] .... OPT_VARS= foo= bar .... ==== [[options-dependencies]] ==== Dependencies, `OPT_DEPTYPE` and `OPT_DEPTYPE_OFF` For any of these dependency types: * `PKG_DEPENDS` * `EXTRACT_DEPENDS` * `PATCH_DEPENDS` * `FETCH_DEPENDS` * `BUILD_DEPENDS` * `LIB_DEPENDS` * `RUN_DEPENDS` When option _OPT_ is selected, the value of `OPT_DEPTYPE`, if defined, is appended to `DEPTYPE`. `OPT_DEPTYPE_OFF` works the same, but when `OPT` is _not_ selected. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_LIB_DEPENDS= liba.so:devel/a OPT1_LIB_DEPENDS_OFF= libb.so:devel/b .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} LIB_DEPENDS+= liba.so:devel/a .else LIB_DEPENDS+= libb.so:devel/b .endif .... [[options-variables]] ==== Generic Variables Replacement, `OPT_VARIABLE` and `OPT_VARIABLE_OFF` For any of these variables: * `ALL_TARGET` * `BINARY_ALIAS` * `BROKEN` * `CATEGORIES` * `CFLAGS` * `CONFIGURE_ENV` * `CONFLICTS` * `CONFLICTS_BUILD` * `CONFLICTS_INSTALL` * `CPPFLAGS` * `CXXFLAGS` * `DESKTOP_ENTRIES` * `DISTFILES` * `EXTRACT_ONLY` * `EXTRA_PATCHES` * `GH_ACCOUNT` * `GH_PROJECT` * `GH_SUBDIR` * `GH_TAGNAME` * `GH_TUPLE` * `GL_ACCOUNT` * `GL_COMMIT` * `GL_PROJECT` * `GL_SITE` * `GL_SUBDIR` * `GL_TUPLE` * `IGNORE` * `INFO` * `INSTALL_TARGET` * `LDFLAGS` * `LIBS` * `MAKE_ARGS` * `MAKE_ENV` * `MASTER_SITES` * `PATCHFILES` * `PATCH_SITES` * `PLIST_DIRS` * `PLIST_FILES` * `PLIST_SUB` * `PORTDOCS` * `PORTEXAMPLES` * `SUB_FILES` * `SUB_LIST` * `TEST_TARGET` * `USES` When option _OPT_ is selected, the value of `OPT_ABOVEVARIABLE`, if defined, is appended to `_ABOVEVARIABLE_`. `OPT_ABOVEVARIABLE_OFF` works the same way, but when `OPT` is _not_ selected. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 OPT1_USES= gmake OPT1_CFLAGS_OFF= -DTEST .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include .if ${PORT_OPTIONS:MOPT1} USES+= gmake .else CFLAGS+= -DTEST .endif .... [NOTE] ==== Some variables are not in this list, in particular `PKGNAMEPREFIX` and `PKGNAMESUFFIX`. This is intentional. A port _must not_ change its name when its option set changes. ==== [WARNING] ==== Some of these variables, at least `ALL_TARGET`, `DISTFILES` and `INSTALL_TARGET`, have their default values set _after_ the options are processed. With these lines in the [.filename]#Makefile#: [.programlisting] .... ALL_TARGET= all DOCS_ALL_TARGET= doc .... If the `DOCS` option is enabled, `ALL_TARGET` will have a final value of `all doc`; if the option is disabled, it would have a value of `all`. With only the options helper line in the [.filename]#Makefile#: [.programlisting] .... DOCS_ALL_TARGET= doc .... If the `DOCS` option is enabled, `ALL_TARGET` will have a final value of `doc`; if the option is disabled, it would have a value of `all`. ==== [[options-targets]] ==== Additional Build Targets, `_target_-_OPT_-on` and `_target_-_OPT_-off` These [.filename]#Makefile# targets can accept optional extra build targets: * `pre-fetch` * `do-fetch` * `post-fetch` * `pre-extract` * `do-extract` * `post-extract` * `pre-patch` * `do-patch` * `post-patch` * `pre-configure` * `do-configure` * `post-configure` * `pre-build` * `do-build` * `post-build` * `pre-install` * `do-install` * `post-install` * `post-stage` * `pre-package` * `do-package` * `post-package` When option _OPT_ is selected, the target `_TARGET_-_OPT_-on`, if defined, is executed after `_TARGET_`. `_TARGET_-_OPT_-off` works the same way, but when `OPT` is _not_ selected. For example: [.programlisting] .... OPTIONS_DEFINE= OPT1 post-patch-OPT1-on: @${REINPLACE_CMD} -e '/opt1/s|/usr/bin/|${EXAMPLESDIR}/|' ${WRKSRC}/Makefile post-patch-OPT1-off: @${REINPLACE_CMD} -e '/opt1/s|/usr/bin/|${PREFIX}/bin/|' ${WRKSRC}/Makefile .... is equivalent to: [.programlisting] .... OPTIONS_DEFINE= OPT1 .include post-patch: .if ${PORT_OPTIONS:MOPT1} @${REINPLACE_CMD} -e '/opt1/s|/usr/bin/|${EXAMPLESDIR}/|' ${WRKSRC}/Makefile .else @${REINPLACE_CMD} -e '/opt1/s|/usr/bin/|${PREFIX}/bin/|' ${WRKSRC}/Makefile .endif .... [[makefile-wrkdir]] == Specifying the Working Directory Each port is extracted into a working directory, which must be writable. The ports system defaults to having `DISTFILES` unpack in to a directory called `${DISTNAME}`. In other words, if the [.filename]#Makefile# has: [.programlisting] .... PORTNAME= foo DISTVERSION= 1.0 .... then the port's distribution files contain a top-level directory, [.filename]#foo-1.0#, and the rest of the files are located under that directory. A number of variables can be overridden if that is not the case. [[makefile-wrksrc]] === `WRKSRC` The variable lists the name of the directory that is created when the application's distfiles are extracted. If our previous example extracted into a directory called [.filename]#foo# (and not [.filename]#foo-1.0#) write: [.programlisting] .... WRKSRC= ${WRKDIR}/foo .... or possibly [.programlisting] .... WRKSRC= ${WRKDIR}/${PORTNAME} .... [[makefile-wrksrc_subdir]] === `WRKSRC_SUBDIR` If the source files needed for the port are in a subdirectory of the extracted distribution file, set `WRKSRC_SUBDIR` to that directory. [.programlisting] .... WRKSRC_SUBDIR= src .... [[makefile-no_wrksubdir]] === `NO_WRKSUBDIR` If the port does not extract in to a subdirectory at all, then set `NO_WRKSUBDIR` to indicate that. [.programlisting] .... NO_WRKSUBDIR= yes .... [NOTE] ==== Because `WRKDIR` is the only directory that is supposed to be writable during the build, and is used to store many files recording the status of the build, the port's extraction will be forced into a subdirectory. ==== [[conflicts]] == Conflict Handling There are three different variables to register a conflict between packages and ports: `CONFLICTS`, `CONFLICTS_INSTALL` and `CONFLICTS_BUILD`. [NOTE] ==== The conflict variables automatically set the variable `IGNORE`, which is more fully documented in crossref:porting-dads[dads-noinstall,Marking a Port Not Installable with `BROKEN`, `FORBIDDEN`, or `IGNORE`]. ==== When removing one of several conflicting ports, it is advisable to retain `CONFLICTS` in those other ports for a few months to cater for users who only update once in a while. [[conclicts-conflicts_install]] `CONFLICTS_INSTALL`:: If the package cannot coexist with other packages (because of file conflicts, runtime incompatibilities, etc.). `CONFLICTS_INSTALL` check is done after the build stage and prior to the install stage. [[conclicts-conflicts_build]] `CONFLICTS_BUILD`:: If the port cannot be built when other specific ports are already installed. Build conflicts are not recorded in the resulting package. [[conclicts-conflicts]] `CONFLICTS`:: If the port cannot be built if a certain port is already installed and the resulting package cannot coexist with the other package. `CONFLICTS` check is done prior to the build stage and prior to the install stage. The most common content of one of these variable is the package base of another port. The package base is the package name without the appended version, it can be obtained by running `make -V PKGBASE`. [[conflicts-ex1]] .Basic usage of `CONFLICTS*` [example] ==== package:dns/bind99[] cannot be installed if package:dns/bind910[] is present because they install same files. First gather the package base to use: [source,shell] .... % make -C dns/bind99 -V PKGBASE bind99 % make -C dns/bind910 -V PKGBASE bind910 .... Then add to the [.filename]#Makefile# of package:dns/bind99[]: [.programlisting] .... CONFLICTS_INSTALL= bind910 .... And add to the [.filename]#Makefile# of package:dns/bind910[]: [.programlisting] .... CONFLICTS_INSTALL= bind99 .... ==== Sometimes, only certain versions of another port are incompatible. When this is the case, use the full package name including the version. If necessary, use shell globs like `*` and `?` so that all necessary versions are matched. [[conflicts-ex2]] .Using `CONFLICTS*` With Globs. [example] ==== From versions from 2.0 and up-to 2.4.1_2, package:deskutils/gnotime[] used to install a bundled version of package:databases/qof[]. To reflect this past, the [.filename]#Makefile# of package:databases/qof[] contains: [.programlisting] .... CONFLICTS_INSTALL= gnotime-2.[0-3]* \ gnotime-2.4.0* gnotime-2.4.1 \ gnotime-2.4.1_[12] .... The first entry match versions `2.0` through `2.3`, the second all the revisions of `2.4.0`, the third the exact `2.4.1` version, and the last the first and second revisions of the `2.4.1` version. package:deskutils/gnotime[] does not have any conflicts line because its current version does not conflict with anything else. ==== The variable `DISABLE_CONFLICTS` may be temporarily set when making targets that are not affected by conflicts. The variable is not to be set in port Makefiles. [source,shell] .... % make -DDISABLE_CONFLICTS patch .... [[install]] == Installing Files [IMPORTANT] ==== The `install` phase is very important to the end user because it adds files to their system. All the additional commands run in the port [.filename]#Makefile#'s `*-install` targets should be echoed to the screen. _Do not_ silence these commands with `@` or `.SILENT`. ==== [[install-macros]] === `INSTALL_*` Macros Use the macros provided in [.filename]#bsd.port.mk# to ensure correct modes of files in the port's `*-install` targets. Set ownership directly in [.filename]#pkg-plist# with the corresponding entries, such as `@(_owner_,_group_,)`, `@owner _owner_`, and `@group _group_`. These operators work until overridden, or until the end of [.filename]#pkg-plist#, so remember to reset them after they are no longer needed. The default ownership is `root:wheel`. See crossref:plist[plist-keywords-base,Base Keywords] for more information. * `INSTALL_PROGRAM` is a command to install binary executables. * `INSTALL_SCRIPT` is a command to install executable scripts. * `INSTALL_LIB` is a command to install shared libraries (but not static libraries). * `INSTALL_KLD` is a command to install kernel loadable modules. Some architectures do not like having the modules stripped, so use this command instead of `INSTALL_PROGRAM`. * `INSTALL_DATA` is a command to install sharable data, including static libraries. * `INSTALL_MAN` is a command to install manpages and other documentation (it does not compress anything). These variables are set to the man:install[1] command with the appropriate flags for each situation. [IMPORTANT] ==== Do not use `INSTALL_LIB` to install static libraries, because stripping them renders them useless. Use `INSTALL_DATA` instead. ==== [[install-strip]] === Stripping Binaries and Shared Libraries Installed binaries should be stripped. Do not strip binaries manually unless absolutely required. The `INSTALL_PROGRAM` macro installs and strips a binary at the same time. The `INSTALL_LIB` macro does the same thing to shared libraries. When a file must be stripped, but neither `INSTALL_PROGRAM` nor `INSTALL_LIB` macros are desirable, `${STRIP_CMD}` strips the program or shared library. This is typically done within the `post-install` target. For example: [.programlisting] .... post-install: ${STRIP_CMD} ${STAGEDIR}${PREFIX}/bin/xdl .... When multiple files need to be stripped: [.programlisting] .... post-install: .for l in geometry media body track world ${STRIP_CMD} ${STAGEDIR}${PREFIX}/lib/lib${PORTNAME}-${l}.so.0 .endfor .... Use man:file[1] on a file to determine if it has been stripped. Binaries are reported by man:file[1] as `stripped`, or `not stripped`. Additionally, man:strip[1] will detect programs that have already been stripped and exit cleanly. [IMPORTANT] ==== When `WITH_DEBUG` is defined, elf files _must not_ be stripped. The variables (`STRIP_CMD`, `INSTALL_PROGRAM`, `INSTALL_LIB`, ...) and crossref:uses[uses,`USES`] provided by the framework handle this automatically. Some software, add `-s` to their `LDFLAGS`, in this case, either remove `-s` if `WITH_DEBUG` is set, or remove it unconditionally and use `STRIP_CMD` in `post-install`. ==== [[install-copytree]] === Installing a Whole Tree of Files Sometimes, a large number of files must be installed while preserving their hierarchical organization. For example, copying over a whole directory tree from `WRKSRC` to a target directory under `PREFIX`. Note that `PREFIX`, `EXAMPLESDIR`, `DATADIR`, and other path variables must always be prepended with `STAGEDIR` to respect staging (see crossref:special[staging,Staging]). Two macros exist for this situation. The advantage of using these macros instead of `cp` is that they guarantee proper file ownership and permissions on target files. The first macro, `COPYTREE_BIN`, will set all the installed files to be executable, thus being suitable for installing into [.filename]#PREFIX/bin#. The second macro, `COPYTREE_SHARE`, does not set executable permissions on files, and is therefore suitable for installing files under [.filename]#PREFIX/share# target. [.programlisting] .... post-install: ${MKDIR} ${STAGEDIR}${EXAMPLESDIR} (cd ${WRKSRC}/examples && ${COPYTREE_SHARE} . ${STAGEDIR}${EXAMPLESDIR}) .... This example will install the contents of the [.filename]#examples# directory in the vendor distfile to the proper examples location of the port. [.programlisting] .... post-install: ${MKDIR} ${STAGEDIR}${DATADIR}/summer (cd ${WRKSRC}/temperatures && ${COPYTREE_SHARE} "June July August" ${STAGEDIR}${DATADIR}/summer) .... And this example will install the data of summer months to the [.filename]#summer# subdirectory of a [.filename]#DATADIR#. Additional `find` arguments can be passed via the third argument to `COPYTREE_*` macros. For example, to install all files from the first example except Makefiles, one can use these commands. [.programlisting] .... post-install: ${MKDIR} ${STAGEDIR}${EXAMPLESDIR} (cd ${WRKSRC}/examples && \ ${COPYTREE_SHARE} . ${STAGEDIR}${EXAMPLESDIR} "! -name Makefile") .... These macros do not add the installed files to [.filename]#pkg-plist#. They must be added manually. For optional documentation (`PORTDOCS`, see <>) and examples (`PORTEXAMPLES`), the `%%PORTDOCS%%` or `%%PORTEXAMPLES%%` prefixes must be prepended in [.filename]#pkg-plist#. [[install-documentation]] === Install Additional Documentation If the software has some documentation other than the standard man and info pages that is useful for the user, install it under `DOCSDIR`. This can be done, like the previous item, in the `post-install` target. Create a new directory for the port. The directory name is `DOCSDIR`. This usually equals `PORTNAME`. However, if the user might want different versions of the port to be installed at the same time, the whole `PKGNAME` can be used. Since only the files listed in [.filename]#pkg-plist# are installed, it is safe to always install documentation to `STAGEDIR` (see crossref:special[staging,Staging]). Hence `.if` blocks are only needed when the installed files are large enough to cause significant I/O overhead. [.programlisting] .... post-install: ${MKDIR} ${STAGEDIR}${DOCSDIR} ${INSTALL_MAN} ${WRKSRC}/docs/xvdocs.ps ${STAGEDIR}${DOCSDIR} .... On the other hand, if there is a DOCS option in the port, install the documentation in a `post-install-DOCS-on` target. These targets are described in <>. Here are some handy variables and how they are expanded by default when used in the [.filename]#Makefile#: * `DATADIR` gets expanded to [.filename]#PREFIX/shared/PORTNAME#. * `DATADIR_REL` gets expanded to [.filename]#share/PORTNAME#. * `DOCSDIR` gets expanded to [.filename]#PREFIX/shared/doc/PORTNAME#. * `DOCSDIR_REL` gets expanded to [.filename]#share/doc/PORTNAME#. * `EXAMPLESDIR` gets expanded to [.filename]#PREFIX/shared/examples/PORTNAME#. * `EXAMPLESDIR_REL` gets expanded to [.filename]#share/examples/PORTNAME#. [NOTE] ==== The `DOCS` option only controls additional documentation installed in `DOCSDIR`. It does not apply to standard man pages and info pages. Things installed in `EXAMPLESDIR` are controlled by the `EXAMPLES` option. ==== These variables are exported to `PLIST_SUB`. Their values will appear there as pathnames relative to [.filename]#PREFIX# if possible. That is, [.filename]#share/doc/PORTNAME# will be substituted for `%%DOCSDIR%%` in the packing list by default, and so on. (See more on [.filename]#pkg-plist# substitution crossref:plist[plist-sub,here].) All conditionally installed documentation files and directories are included in [.filename]#pkg-plist# with the `%%PORTDOCS%%` prefix, for example: [.programlisting] .... %%PORTDOCS%%%%DOCSDIR%%/AUTHORS %%PORTDOCS%%%%DOCSDIR%%/CONTACT .... As an alternative to enumerating the documentation files in [.filename]#pkg-plist#, a port can set the variable `PORTDOCS` to a list of file names and shell glob patterns to add to the final packing list. The names will be relative to `DOCSDIR`. Therefore, a port that utilizes `PORTDOCS`, and uses a non-default location for its documentation, must set `DOCSDIR` accordingly. If a directory is listed in `PORTDOCS` or matched by a glob pattern from this variable, the entire subtree of contained files and directories will be registered in the final packing list. If the `DOCS` option has been unset then files and directories listed in `PORTDOCS` would not be installed or added to port packing list. Installing the documentation at `PORTDOCS` as shown above remains up to the port itself. A typical example of utilizing `PORTDOCS`: [.programlisting] .... PORTDOCS= README.* ChangeLog docs/* .... [NOTE] ==== The equivalents of `PORTDOCS` for files installed under `DATADIR` and `EXAMPLESDIR` are `PORTDATA` and `PORTEXAMPLES`, respectively. The contents of [.filename]#pkg-message# are displayed upon installation. See crossref:pkg-files[porting-message,the section on using [.filename]#pkg-message#] for details. [.filename]#pkg-message# does not need to be added to [.filename]#pkg-plist#. ==== [[install-subdirs]] === Subdirectories Under `PREFIX` Try to let the port put things in the right subdirectories of `PREFIX`. Some ports lump everything and put it in the subdirectory with the port's name, which is incorrect. Also, many ports put everything except binaries, header files and manual pages in a subdirectory of [.filename]#lib#, which does not work well with the BSD paradigm. Many of the files must be moved to one of these directories: [.filename]#etc# (setup/configuration files), [.filename]#libexec# (executables started internally), [.filename]#sbin# (executables for superusers/managers), [.filename]#info# (documentation for info browser) or [.filename]#share# (architecture independent files). See man:hier[7] for details; the rules governing [.filename]#/usr# pretty much apply to [.filename]#/usr/local# too. The exception are ports dealing with USENET "news". They may use [.filename]#PREFIX/news# as a destination for their files. [[binary-alias]] == Use `BINARY_ALIAS` to Rename Commands Instead of Patching the Build When `BINARY_ALIAS` is defined it will create symlinks of the given commands in a directory which will be prepended to `PATH`. Use it to substitute hardcoded commands the build phase relies on without having to patch any build files. [[binary-alias-ex1]] .Using `BINARY_ALIAS` to Make `gsed` Available as `sed` [example] ==== Some ports expect `sed` to behave like GNU sed and use features that man:sed[1] does not provide. GNU sed is available from package:textproc/gsed[] on FreeBSD. Use `BINARY_ALIAS` to substitute `sed` with `gsed` for the duration of the build: [.programlisting] .... BUILD_DEPENDS= gsed:textproc/gsed ... BINARY_ALIAS= sed=gsed .... ==== [[binary-alias-ex2]] .Using `BINARY_ALIAS` to Provide Aliases for Hardcoded `python3` Commands [example] ==== A port that has a hardcoded reference to `python3` in its build scripts will need to have it available in `PATH` at build time. Use `BINARY_ALIAS` to create an alias that points to the right Python 3 binary: [.programlisting] .... USES= python:3.4+,build ... BINARY_ALIAS= python3=${PYTHON_CMD} .... See crossref:special[using-python,Using Python] for more information about `USES=python`. ==== [NOTE] ==== Binary aliases are created after the dependencies provided via `BUILD_DEPENDS` and `LIB_DEPENDS` are processed and before the `configure` target. This leads to various limitations. For example, programs installed via `TEST_DEPENDS` cannot be used to create a binary alias as test dependencies specified this way are processed after binary aliases are created. ==== diff --git a/documentation/content/zh-tw/books/porters-handbook/pkg-files/_index.adoc b/documentation/content/zh-tw/books/porters-handbook/pkg-files/_index.adoc index 6c0e4215b2..cef2253cbc 100644 --- a/documentation/content/zh-tw/books/porters-handbook/pkg-files/_index.adoc +++ b/documentation/content/zh-tw/books/porters-handbook/pkg-files/_index.adoc @@ -1,321 +1,321 @@ --- title: Chapter 9. pkg-* prev: books/porters-handbook/plist next: books/porters-handbook/testing description: Tricks about the pkg-* files tags: ["pkg", "pkg-message", "UCL", "pkg-install", "pkg-deinstall"] showBookMenu: true weight: 9 params: path: "/books/porters-handbook/pkg-files/" --- [[pkg-files]] = pkg-* :doctype: book :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumoffset: 9 :partnums: :source-highlighter: rouge :experimental: :images-path: books/porters-handbook/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] There are some tricks we have not mentioned yet about the [.filename]#pkg-*# files that come in handy sometimes. [[porting-message]] == pkg-message To display a message when the package is installed, place the message in [.filename]#pkg-message#. This capability is often useful to display additional installation steps to be taken after a `pkg install` or `pkg upgrade`. [IMPORTANT] ==== * [.filename]#pkg-message# must contain only information that is _vital_ to setup and operation on FreeBSD, and that is unique to the port in question. * Setup information should only be shown on initial install. Upgrade instructions should be shown only when upgrading from the relevant version. * Do not surround the messages with either whitespace or lines of symbols (like `----------`, `**********`, or `==========`). Leave the formatting to man:pkg[8]. * Committers have blanket approval to constrain existing messages to install or upgrade ranges using the UCL format specifications. ==== pkg-message supports two formats: raw:: A regular plain text file. Its message is only displayed on install. UCL:: If the file starts with "`[`" then it is considered to be a UCL file. The UCL format is described on https://github.com/vstakhov/libucl[libucl's GitHub page]. [NOTE] ==== Do not add an entry for [.filename]#pkg-message# in [.filename]#pkg-plist#. ==== [[porting-message-ucl]] === UCL in pkg-message The format is the following. It should be an array of objects. The objects themselves can have these keywords: `message`:: The actual message to be displayed. This keyword is mandatory. `type`:: When the message should be displayed. `maximum_version`:: Only if `type` is `upgrade`. Display if upgrading from a version strictly lower than the version specified. `minimum_version`:: Only if `type` is `upgrade`. Display if upgrading from a version stictly greater than the version specified. The `maximum_version` and `minimum_version` keywords can be combined. The `type` keyword can have three values: `install`:: The message should only be displayed when the package is installed. `remove`:: The message should only be displayed when the package is removed. `upgrade`:: the message should only be displayed during an upgrade of the package.. [IMPORTANT] ==== To preserve the compatibility with non UCL [.filename]#pkg-message# files, the first line of a UCL [.filename]#pkg-message# _MUST be_ a single "`[`", and the last line _MUST be_ a single "`]`". ==== [[porting-message-ucl-short-ex]] .UCL Short Strings [example] ==== The message is delimited by double quotes `"`, this is used for simple single line strings: [.programlisting] .... [ { type: install message: "Simple message" } ] .... ==== [[porting-message-ucl-multiline-ex]] .UCL Multiline Strings [example] ==== Multiline strings use the standard here document notation. The multiline delimiter _must_ start just after `<<` symbols without any whitespace and it _must_ consist of capital letters only. To finish a multiline string, add the delimiter string on a line of its own without any whitespace. The message from <> can be written as: [.programlisting] .... [ { type: install message: < 1.0 and < 3.0 remove that file." } ] .... [IMPORTANT] -**** +===== When displaying a message on upgrade, it is important to limit when it is being shown to the user. Most of the time it is by using `maximum_version` to limit its usage to upgrades from before a certain version when something specific needs to be done. -**** +===== ==== [[pkg-install]] == pkg-install If the port needs to execute commands when the binary package is installed with `pkg add` or `pkg install`, use [.filename]#pkg-install#. This script will automatically be added to the package. It will be run twice by `pkg`, the first time as `${SH} pkg-install ${PKGNAME} PRE-INSTALL` before the package is installed, and the second time as `${SH} pkg-install ${PKGNAME} POST-INSTALL` after it has been installed. `$2` can be tested to determine which mode the script is being run in. The `PKG_PREFIX` environmental variable will be set to the package installation directory. [IMPORTANT] ==== This script is here to help you set up the package so that it is as ready to use as possible. It _must not_ be abused to start services, stop services, or run any other commands that will modify the currently running system. ==== [[pkg-deinstall]] == pkg-deinstall This script executes when a package is removed. This script will be run twice by `pkg delete`. The first time as `${SH} pkg-deinstall ${PKGNAME} DEINSTALL` before the port is de-installed and the second time as `${SH} pkg-deinstall ${PKGNAME} POST-DEINSTALL` after the port has been de-installed. `$2` can be tested to determine which mode the script is being run in. The `PKG_PREFIX` environmental variable will be set to the package installation directory [IMPORTANT] ==== This script is here to help you set up the package so that it is as ready to use as possible. It _must not_ be abused to start services, stop services, or run any other commands that will modify the currently running system. ==== [[pkg-names]] == Changing the Names of pkg-* All the names of [.filename]#pkg-\*# are defined using variables that can be changed in the [.filename]#Makefile# if needed. This is especially useful when sharing the same [.filename]#pkg-*# files among several ports or when it is necessary to write to one of these files. See crossref:porting-dads[porting-wrkdir,writing to places other than `WRKDIR`] for why it is a bad idea to write directly into the directory containing the [.filename]#pkg-*# files. Here is a list of variable names and their default values. (`PKGDIR` defaults to `${MASTERDIR}`.) [.informaltable] [cols="1,1", frame="none", options="header"] |=== | Variable | Default value |`DESCR` |`${PKGDIR}/pkg-descr` |`PLIST` |`${PKGDIR}/pkg-plist` |`PKGINSTALL` |`${PKGDIR}/pkg-install` |`PKGDEINSTALL` |`${PKGDIR}/pkg-deinstall` |`PKGMESSAGE` |`${PKGDIR}/pkg-message` |=== [[using-sub-files]] == Making Use of `SUB_FILES` and `SUB_LIST` `SUB_FILES` and `SUB_LIST` are useful for dynamic values in port files, such as the installation `PREFIX` in [.filename]#pkg-message#. `SUB_FILES` specifies a list of files to be automatically modified. Each [.filename]#file# in the `SUB_FILES` list must have a corresponding [.filename]#file.in# present in `FILESDIR`. A modified version will be created as [.filename]#${WRKDIR}/file#. Files defined as a value of `USE_RC_SUBR` are automatically added to `SUB_FILES`. For the files [.filename]#pkg-message#, [.filename]#pkg-install#, and [.filename]#pkg-deinstall#, the corresponding Makefile variable is automatically set to point to the processed version. `SUB_LIST` is a list of `VAR=VALUE` pairs. For each pair, `%%VAR%%` will be replaced with `VALUE` in each file listed in `SUB_FILES`. Several common pairs are automatically defined: `PREFIX`, `LOCALBASE`, `DATADIR`, `DOCSDIR`, `EXAMPLESDIR`, `WWWDIR`, and `ETCDIR`. Any line beginning with `@comment` followed by a space, will be deleted from resulting files after a variable substitution. This example replaces `%%ARCH%%` with the system architecture in a [.filename]#pkg-message#: [.programlisting] .... SUB_FILES= pkg-message SUB_LIST= ARCH=${ARCH} .... Note that for this example, [.filename]#pkg-message.in# must exist in `FILESDIR`. Example of a good [.filename]#pkg-message.in#: [.programlisting] .... Now it is time to configure this package. Copy %%PREFIX%%/shared/examples/putsy/%%ARCH%%.conf into your home directory as .putsy.conf and edit it. .... diff --git a/documentation/content/zh-tw/books/porters-handbook/quick-porting/_index.adoc b/documentation/content/zh-tw/books/porters-handbook/quick-porting/_index.adoc index 5466ede270..dd11a4e87e 100644 --- a/documentation/content/zh-tw/books/porters-handbook/quick-porting/_index.adoc +++ b/documentation/content/zh-tw/books/porters-handbook/quick-porting/_index.adoc @@ -1,296 +1,296 @@ --- title: 章 3. 打造 Port 快速上手篇 prev: books/porters-handbook/new-port next: books/porters-handbook/slow-porting showBookMenu: true weight: 3 params: path: "/books/porters-handbook/quick-porting/" --- [[quick-porting]] = 打造 Port 快速上手篇 :doctype: book :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumoffset: 3 :partnums: :source-highlighter: rouge :experimental: :images-path: books/porters-handbook/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] 本節主要介紹如何來快速打造 port,然而實際應用時這快速方法可能不足,完整的 "慢速打造 Port" 的步驟在 crossref:slow-porting[slow-porting,Slow Porting] 詳述。 首先取得該應用程式的原始程式碼壓縮檔(tarball),並把它放到 `DISTDIR`,預設路徑應該是 [.filename]#/usr/ports/distfiles#. [NOTE] ==== 這些步驟假設軟體可以直接編譯。也就是不需要任何修改就可以直接在 FreeBSD 上執行。如果需要修改,請參見crossref:slow-porting[slow-porting,Slow Porting]。 ==== [NOTE] ==== 在 porting 之前,建議在 [.filename]#/etc/make.conf# 設定 `DEVELOPER` man:make[1] 變數。 [source,shell] .... # echo DEVELOPER=yes >> /etc/make.conf .... 這個設定開啟了 "developer mode" ,顯示已棄用警告並在使用 `make` 時檢查品質。 ==== [[porting-makefile]] == 編寫 [.filename]#Makefile# 最簡單的 [.filename]#Makefile# 大概是像這樣: [.programlisting] .... # $FreeBSD: head/zh_TW.UTF-8/books/porters-handbook/book.xml 48496 2016-03-29 01:37:53Z kevlo $ PORTNAME= oneko PORTVERSION= 1.1b CATEGORIES= games MASTER_SITES= ftp://ftp.cs.columbia.edu/archives/X11R5/contrib/ MAINTAINER= youremail@example.com COMMENT= Cat chasing a mouse all over the screen .include .... 嘗試了解此檔案,有關這點的細節部份,可以參閱 crossref:porting-samplem[porting-samplem,sample Makefile] 章節。 [[porting-desc]] == 撰寫說明檔 無論是否打算再加工做成 package,有兩個檔案是任何 port 都必須要具備的。 這兩個檔分別是 [.filename]#pkg-descr# 及 [.filename]#pkg-plist# 。 他們檔名前面都有 [.filename]#pkg-# 以跟其他檔案做區別。 [[porting-pkg-descr]] === [.filename]#pkg-descr# 這是此 port 的詳細說明檔,請用一段或幾段文字來說明該 port 的作用 [NOTE] ==== 請注意,這檔_絕非_「該軟體的說明手冊」或是「如何編譯、使用該 port 的說明」! _若是從該軟體的 [.filename]#README# 或 manpage 直接複製過來的話,請注意_。他們常常不是正確的 port 描述或是格式並不適合。例如,manpage會對齊空白,這用monospace字型來看會特別糟糕。 另一方面, [.filename]#pkg-descr# 的內容必須比 crossref:makefiles[makefile-comment,COMMENT line from the Makefile] 還要詳細,要能詳細解釋此 port 相關的內容。 ==== 一個良好的 [.filename]#pkg-descr# 可以讓使用者清楚的了解軟體的功能而無須查詢文件或者訪問網站,提及特定的需求像是圖形工具、依賴、執行的環境跟實作的語言,能大大的幫助使用者了解這個 port 是怎麼運作的。 [NOTE] ==== 過去在 #pkg-descr# 文件最後一行引入的 URL 已經移到 [.filename]#Makefile# 裡面了。 ==== [[porting-pkg-plist]] === [.filename]#pkg-plist# 這是該 port 所會裝的所有檔案清單,另外因為 package 會由這清單所產生,因此也被稱為『"packing list (打包清單)"』。路徑是相對於安裝的 prefix (通常是 [.filename]#/usr/local# )。 這是一個簡單的例子: [.programlisting] .... bin/oneko man/man1/oneko.1.gz lib/X11/app-defaults/Oneko lib/X11/oneko/cat1.xpm lib/X11/oneko/cat2.xpm lib/X11/oneko/mouse.xpm .... 關於 packing list 方面,可以詳閱 man:pkg-create[8] manual page 。 [NOTE] ==== -建議清單內的檔名,依照字母順序作排序,那麼下次要升級時, 會比較清楚、方便來更新這份清單。 +建議清單內的檔名,依照字母順序作排序,那麼下次要升級時, 會比較清楚、方便來更新這份清單。 ==== [TIP] ==== 手動產生這份清單實在太苦了。尤其若該 port 會裝一大堆檔案的話, 請多善用 <> 會比較省時省力唷。 ==== 只有在一種情況下可以省略 [.filename]#pkg-plist# 檔: 若安裝的 port 相當單純,只有裝一些檔案,那麼可以在 [.filename]#Makefile# 內改用 `PLIST_FILES` 來取代。 比如,可以在上述的 [.filename]#oneko# port 內不必附上 [.filename]#pkg-plist# ,而只需在 [.filename]#Makefile# 內加入下列幾行: [.programlisting] .... PLIST_FILES= bin/oneko \ man/man1/oneko.1.gz \ lib/X11/app-defaults/Oneko \ lib/X11/oneko/cat1.xpm \ lib/X11/oneko/cat2.xpm \ lib/X11/oneko/mouse.xpm .... [NOTE] ==== `PLIST_FILES` 的使用不應該被濫用,人們通常透過在 ports tree 的 [.filename]#pkg-plist# 文件來了解 port ,在 [.filename]#Makefile# 中使用 `PLIST_FILES` 敘述會增加搜尋的難度。 ==== [TIP] ==== 如果 port 需要創建空的目錄,或者安裝時在 [.filename]#${PREFIX}# 之外創建目錄,請參考 crossref:plist[plist-dir-cleaning, Cleaning Up Empty Directories] 來獲得更多資訊。 ==== [TIP] ==== 因為 `PLIST_FILES` 是 man:make[1] 的變數,任何在此條目的 sapce 都必須被引入。舉例來說,如果使用 man:pkg-create[8] 和 crossref:plist[plist-keywords, Expanding Package List with Keywords] 描述的關鍵字,以下的條目必須被引入。 [.programlisting] .... PLIST_FILES= "@sample ${ETCDIR}/oneko.conf.sample" .... ==== 後面會介紹到如何運用 [.filename]#pkg-plist#、 `PLIST_FILES` 這些技巧以因應crossref:plist[plist,更複雜的狀況]。 [[porting-checksum]] == 產生 checksum 檔 只要打 `make makesum` 就好了, 接下來就會自動產生相對應的 [.filename]#distinfo# 檔了唷 。 [[porting-testing]] == 測試 Port 接下來,必須檢驗是否有符合 port 的遊戲規則,包括打包該 port 為 package。 以下有幾個需要確認的重要地方: * 若該 port 沒裝的東西,不要列在 [.filename]#pkg-plist# 內。 * 若該 port 有裝的東西,請務必列在 [.filename]#pkg-plist# 內。 * port 可以透過 `install` 來安裝,這可以確保安裝的 script 是正常運作的。 * port 可以透過 `uninstall` 來移除,這可以確保安裝的 script 是正常運作的。 * port 只有在 `featch` 目標時才能使用網路,這對 package builders 很重要,像是 package:ports-mgmt/poudriere[]. * 確保 `make package` 可以被一般使用者執行 (也就是說,非 `root` 使用者),如果上述執行失敗,軟體很可能需要修改,請參考 crossref:uses[uses-fakeroot,`fakeroot`] 和 crossref:uses[uses-uidfix,`uidfix`]。 [.procedure] ==== *Procedure: 建議的測試順序* . `make stage` . `make check-orphans` . `make package` . `make install` . `make deinstall` . `pkg add _package-filename_` . `make package` (as user) ==== 確認在任何階段都沒有任何警告出現。 可以透過 package:ports-mgmt/tinderbox[] 或者 Ports Collection 的 package:ports-mgmt/poudriere[] 工具來完成自動化測試,參考 crossref:testing[testing-poudriere,Poudriere] 獲得更多的資訊。她維護 `jails` ,可以在不影響系統的情況下執行上述所有的測試。 [[porting-portlint]] == 以 `portlint` 來作檢查 Port 請用 `portlint` 來檢查該 port 是否有遵循我們的規則。 package:ports-mgmt/portlint[] 是 ports collection 的其中一個套件。 它主要可以用來檢驗 <> 內容是否正確以及 <> 是否有正確命名。 [IMPORTANT] ==== 不要盲目的相信 `portlint` 的輸出,他是一個 static lint 工具,並且有可能會出錯。 ==== [[porting-submitting]] == 提交新的 Port 提交新的 Port 前,請閱讀 <> 章節。 現在你很高興終於打造出 port 來囉,唯一剩下要做的就是把它正式放到 FreeBSD ports tree 內,才能讓每個人都能分享使用這個 port。 [IMPORTANT] ==== 我們不需要 [.filename]#work# 目錄或是檔名像 [.filename]#pkgname.tgz# 的 package ,請現在刪除他們。 ==== 下一步,創建一個 man:patch[1] ,一個文件,假設此 port 叫做 `oneko` 並且屬於 `games` 類別。 [[porting-submitting-diff]] .為新的 Port 創建 [.filename]#.diff# [example] ==== 透過 `git add .` 加入所有的檔案,然後透過 `git diff` 來檢查不同處,如下。 [source,shell] .... % git add . % git diff --staged .... 確保所有需要的檔案都被引入了,將更改提交到你本地分支並使用 `git format-patch` 產生 patch。 [source,shell] .... % git commit % git format-patch origin/main .... 使用 `git format-patch` 產生的 patch 會包含作者以及電子郵件,讓其他開發者更容易使用 (with `git am`) 以及給予回饋。 [IMPORTANT] -**** +==== 為了讓開發者更容易在他們的 ports tree 使用此 patch ,請基於你的 ports tree 來產生 [.filename]#.diff# 。 -**** +==== ==== 透過 https://bugs.freebsd.org/submit/[bug submission form] 繳交 [.filename]#oneko.diff# 。使用 "Ports & Packages" 產品和 "Individual Port(s)" 組件,然後跟著這裡的教學走,在 PR 的描述欄位 (也許是較短的 `COMMENT` ) 加入簡短的敘述,並且記得在附件中加入 [.filename]#oneko.diff# 。 [NOTE] ==== 在問題回報系統中給一個好的總結可以讓 port 開發者更輕鬆的處理此 port ,我們預期的描述形式為 "[NEW PORT] _category/portname short description of the port_" ,使用此形式能讓我們更快的部屬新的 port 。 ==== 送出 port 之後,請耐心等候佳音。 有時候可能需要等個幾天或幾個月時間,才會在 FreeBSD ports tree 上正式出現。 等待中的 port PR 清單可以在 http://www.FreeBSD.org/cgi/query-pr-summary.cgi?category=ports[] 查閱。 在搜尋表單中選擇 _Open_ and _Ports & Packages_ 來獲得 _open_ port PRs 的清單。 在看過新的 port 之後,如果需要的話,我們會回覆您,然後會將它提交到 port tree 。 您的大名會被列在 extref:{contributors}[Additional FreeBSD Contributors, contrib-additional] 列表上,以及其他檔案中。 也可以透過 man:shar[1] 檔案來提交 port ,請使用之前 `oneko` port 的例子。 .為新的 Port 創建 [.filename]#.shar# [[porting-submitting-shar]] [example] ==== 前往 port 所在的目錄,使用 `tar`創建 shar archive: [source,shell] .... % cd .. % tar cf oneko.shar --format shar oneko .... ==== 可以使用跟提交 [.filename]#oneko.diff# 一樣的方式來提交 [.filename]#oneko.shar# 。