[ADD] cache qemu : miroir des téléchargements des VM, hors ligne compris
VMs of one host pulled the same packages again and again, and an install
could not be proven to work without the network. A Go service intercepts the
bridge transparently: a package file is served from disk, an index is always
revalidated upstream and replayed only when upstream is mute, git is mirrored.
Deployment poses the cache authority in QEMU and Proxmox VE guests, and can cut
the network for a whole install. The TODO menu installs, diagnoses, fills,
cleans and copies the store; long_test/qemu_cache.py measures that a second VM
pulls no package upstream. Covered by Go tests and test/test_qemu_cache_*.py.
--- FR ---
Les VM d'un hôte tiraient sans cesse les mêmes paquets, et rien ne prouvait
qu'une installation marche sans réseau. Un service Go intercepte le pont de
façon transparente : un fichier de paquet est servi du disque, un index est
toujours revalidé à l'amont et rejoué seulement quand l'amont est muet, git
est mis en miroir. Le déploiement pose l'autorité du cache dans les invités
QEMU et Proxmox VE, et peut couper le réseau pour toute une installation. Le
menu TODO installe, diagnostique, remplit, nettoie et emporte le magasin ;
long_test/qemu_cache.py mesure qu'une seconde VM ne tire aucun paquet de
l'amont. Couvert par les tests Go et test/test_qemu_cache_*.py.
Assisted-by: Claude Opus 5
2026-09-14 14:41:19 -04:00
|
|
|
// © 2026 TechnoLibre (http://www.technolibre.ca)
|
|
|
|
|
// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
|
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bufio"
|
|
|
|
|
"fmt"
|
|
|
|
|
"net"
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"sort"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Le détournement est TRANSPARENT : il vaut pour tout ce qui sort du pont, et
|
|
|
|
|
// une VM n'a aucun moyen de s'y soustraire depuis l'intérieur. Ne pas lui
|
|
|
|
|
// donner l'autorité ne la dispense pas d'être interceptée — cela lui fait
|
|
|
|
|
// seulement refuser un certificat qu'elle ne reconnaît pas, et échouer sur
|
|
|
|
|
// « self-signed certificate in certificate chain ».
|
|
|
|
|
//
|
|
|
|
|
// Une exception doit donc être posée sur l'HÔTE, avant que la VM démarre, et
|
|
|
|
|
// elle a besoin d'un identifiant que la VM porte de façon stable. L'adresse
|
|
|
|
|
// IP ne convient pas : elle vient d'un bail qui change. L'adresse MAC est
|
|
|
|
|
// fixée dans la définition du domaine et ne bouge plus.
|
|
|
|
|
//
|
|
|
|
|
// Elle est tenue dans un ensemble nommé nftables plutôt que dans les règles
|
|
|
|
|
// elles-mêmes : un élément s'ajoute et se retire à chaud, sans reposer le
|
|
|
|
|
// jeu de règles, donc sans couper les téléchargements des autres VM.
|
|
|
|
|
|
|
|
|
|
// BypassSetName : le nom de l'ensemble à l'intérieur de la table.
|
|
|
|
|
const BypassSetName = "bypass"
|
|
|
|
|
|
|
|
|
|
// BypassEntry associe l'adresse MAC soustraite au nom de la VM qui la porte.
|
|
|
|
|
//
|
|
|
|
|
// Le nom n'entre dans aucune règle ; il existe pour que la liste soit
|
|
|
|
|
// relisible et pour que l'on sache quelle entrée retirer quand la VM meurt.
|
|
|
|
|
// Une MAC orpheline est le mode de défaillance de ce fichier : réattribuée à
|
|
|
|
|
// une VM neuve, elle la soustrairait au cache sans que personne l'ait
|
|
|
|
|
// demandé, et rien ne le dirait.
|
|
|
|
|
type BypassEntry struct {
|
|
|
|
|
MAC string
|
|
|
|
|
Name string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NormalizeMAC rend la forme canonique — six octets, minuscules,
|
|
|
|
|
// deux-points — ou dit pourquoi l'entrée ne peut pas en être une.
|
|
|
|
|
//
|
|
|
|
|
// Deux refus qui ne sont pas des caprices. L'adresse nulle correspond à tout
|
|
|
|
|
// ce qui n'a pas d'adresse et n'identifie donc rien. Une adresse de diffusion
|
|
|
|
|
// ou de groupe — bit de poids faible du premier octet à 1 — ne peut JAMAIS
|
|
|
|
|
// apparaître comme adresse SOURCE : l'entrée serait morte à l'écriture, et
|
|
|
|
|
// une exception qui ne s'applique jamais est pire qu'absente, puisqu'on la
|
|
|
|
|
// croit posée.
|
|
|
|
|
func NormalizeMAC(s string) (string, error) {
|
|
|
|
|
brut := strings.TrimSpace(s)
|
|
|
|
|
if brut == "" {
|
[ADD] cache qemu : messages du binaire en anglais ou en français
Le binaire ne parlait que français alors que le menu suit EL_LANG : un
opérateur anglophone lisait un statut qu'il ne comprenait pas. Chaque message
humain passe par T(), dont la clé EST le texte français, et un catalogue
anglais le traduit ; la langue vient de --lang, puis d'EL_LANG, le français
restant le défaut. Le menu demande le français pour ce qu'il RELIT, ses
motifs ne connaissant que cette langue. Un test refuse un message sans
traduction, une traduction orpheline et un verbe de format qui diverge.
--- EN ---
The binary spoke French only while the menu follows EL_LANG: an English
reader got a status he could not read. Every human message goes through T(),
whose key IS the French text, and an English catalogue translates it; the
language comes from --lang, then EL_LANG, French remaining the default. The
menu asks for French for what it READS BACK, its patterns knowing that
language alone. A test refuses a message without translation, an orphan
translation, and a format verb that drifts.
Assisted-by: Claude Opus 5
2026-09-16 02:59:01 -04:00
|
|
|
return "", fmt.Errorf("%s", T("adresse MAC vide"))
|
[ADD] cache qemu : miroir des téléchargements des VM, hors ligne compris
VMs of one host pulled the same packages again and again, and an install
could not be proven to work without the network. A Go service intercepts the
bridge transparently: a package file is served from disk, an index is always
revalidated upstream and replayed only when upstream is mute, git is mirrored.
Deployment poses the cache authority in QEMU and Proxmox VE guests, and can cut
the network for a whole install. The TODO menu installs, diagnoses, fills,
cleans and copies the store; long_test/qemu_cache.py measures that a second VM
pulls no package upstream. Covered by Go tests and test/test_qemu_cache_*.py.
--- FR ---
Les VM d'un hôte tiraient sans cesse les mêmes paquets, et rien ne prouvait
qu'une installation marche sans réseau. Un service Go intercepte le pont de
façon transparente : un fichier de paquet est servi du disque, un index est
toujours revalidé à l'amont et rejoué seulement quand l'amont est muet, git
est mis en miroir. Le déploiement pose l'autorité du cache dans les invités
QEMU et Proxmox VE, et peut couper le réseau pour toute une installation. Le
menu TODO installe, diagnostique, remplit, nettoie et emporte le magasin ;
long_test/qemu_cache.py mesure qu'une seconde VM ne tire aucun paquet de
l'amont. Couvert par les tests Go et test/test_qemu_cache_*.py.
Assisted-by: Claude Opus 5
2026-09-14 14:41:19 -04:00
|
|
|
}
|
|
|
|
|
// Douze caractères hexadécimaux sans séparateur : la forme que rendent
|
|
|
|
|
// plusieurs outils, que net.ParseMAC ne lit pas.
|
|
|
|
|
if len(brut) == 12 && !strings.ContainsAny(brut, ":-.") {
|
|
|
|
|
var parts []string
|
|
|
|
|
for i := 0; i < 12; i += 2 {
|
|
|
|
|
parts = append(parts, brut[i:i+2])
|
|
|
|
|
}
|
|
|
|
|
brut = strings.Join(parts, ":")
|
|
|
|
|
}
|
|
|
|
|
adr, err := net.ParseMAC(brut)
|
|
|
|
|
if err != nil {
|
[ADD] cache qemu : messages du binaire en anglais ou en français
Le binaire ne parlait que français alors que le menu suit EL_LANG : un
opérateur anglophone lisait un statut qu'il ne comprenait pas. Chaque message
humain passe par T(), dont la clé EST le texte français, et un catalogue
anglais le traduit ; la langue vient de --lang, puis d'EL_LANG, le français
restant le défaut. Le menu demande le français pour ce qu'il RELIT, ses
motifs ne connaissant que cette langue. Un test refuse un message sans
traduction, une traduction orpheline et un verbe de format qui diverge.
--- EN ---
The binary spoke French only while the menu follows EL_LANG: an English
reader got a status he could not read. Every human message goes through T(),
whose key IS the French text, and an English catalogue translates it; the
language comes from --lang, then EL_LANG, French remaining the default. The
menu asks for French for what it READS BACK, its patterns knowing that
language alone. A test refuses a message without translation, an orphan
translation, and a format verb that drifts.
Assisted-by: Claude Opus 5
2026-09-16 02:59:01 -04:00
|
|
|
return "", fmt.Errorf(T("adresse MAC illisible %q"), s)
|
[ADD] cache qemu : miroir des téléchargements des VM, hors ligne compris
VMs of one host pulled the same packages again and again, and an install
could not be proven to work without the network. A Go service intercepts the
bridge transparently: a package file is served from disk, an index is always
revalidated upstream and replayed only when upstream is mute, git is mirrored.
Deployment poses the cache authority in QEMU and Proxmox VE guests, and can cut
the network for a whole install. The TODO menu installs, diagnoses, fills,
cleans and copies the store; long_test/qemu_cache.py measures that a second VM
pulls no package upstream. Covered by Go tests and test/test_qemu_cache_*.py.
--- FR ---
Les VM d'un hôte tiraient sans cesse les mêmes paquets, et rien ne prouvait
qu'une installation marche sans réseau. Un service Go intercepte le pont de
façon transparente : un fichier de paquet est servi du disque, un index est
toujours revalidé à l'amont et rejoué seulement quand l'amont est muet, git
est mis en miroir. Le déploiement pose l'autorité du cache dans les invités
QEMU et Proxmox VE, et peut couper le réseau pour toute une installation. Le
menu TODO installe, diagnostique, remplit, nettoie et emporte le magasin ;
long_test/qemu_cache.py mesure qu'une seconde VM ne tire aucun paquet de
l'amont. Couvert par les tests Go et test/test_qemu_cache_*.py.
Assisted-by: Claude Opus 5
2026-09-14 14:41:19 -04:00
|
|
|
}
|
|
|
|
|
if len(adr) != 6 {
|
[ADD] cache qemu : messages du binaire en anglais ou en français
Le binaire ne parlait que français alors que le menu suit EL_LANG : un
opérateur anglophone lisait un statut qu'il ne comprenait pas. Chaque message
humain passe par T(), dont la clé EST le texte français, et un catalogue
anglais le traduit ; la langue vient de --lang, puis d'EL_LANG, le français
restant le défaut. Le menu demande le français pour ce qu'il RELIT, ses
motifs ne connaissant que cette langue. Un test refuse un message sans
traduction, une traduction orpheline et un verbe de format qui diverge.
--- EN ---
The binary spoke French only while the menu follows EL_LANG: an English
reader got a status he could not read. Every human message goes through T(),
whose key IS the French text, and an English catalogue translates it; the
language comes from --lang, then EL_LANG, French remaining the default. The
menu asks for French for what it READS BACK, its patterns knowing that
language alone. A test refuses a message without translation, an orphan
translation, and a format verb that drifts.
Assisted-by: Claude Opus 5
2026-09-16 02:59:01 -04:00
|
|
|
return "", fmt.Errorf(T("adresse MAC de %d octets, six attendus"), len(adr))
|
[ADD] cache qemu : miroir des téléchargements des VM, hors ligne compris
VMs of one host pulled the same packages again and again, and an install
could not be proven to work without the network. A Go service intercepts the
bridge transparently: a package file is served from disk, an index is always
revalidated upstream and replayed only when upstream is mute, git is mirrored.
Deployment poses the cache authority in QEMU and Proxmox VE guests, and can cut
the network for a whole install. The TODO menu installs, diagnoses, fills,
cleans and copies the store; long_test/qemu_cache.py measures that a second VM
pulls no package upstream. Covered by Go tests and test/test_qemu_cache_*.py.
--- FR ---
Les VM d'un hôte tiraient sans cesse les mêmes paquets, et rien ne prouvait
qu'une installation marche sans réseau. Un service Go intercepte le pont de
façon transparente : un fichier de paquet est servi du disque, un index est
toujours revalidé à l'amont et rejoué seulement quand l'amont est muet, git
est mis en miroir. Le déploiement pose l'autorité du cache dans les invités
QEMU et Proxmox VE, et peut couper le réseau pour toute une installation. Le
menu TODO installe, diagnostique, remplit, nettoie et emporte le magasin ;
long_test/qemu_cache.py mesure qu'une seconde VM ne tire aucun paquet de
l'amont. Couvert par les tests Go et test/test_qemu_cache_*.py.
Assisted-by: Claude Opus 5
2026-09-14 14:41:19 -04:00
|
|
|
}
|
|
|
|
|
if adr[0]&1 == 1 {
|
|
|
|
|
return "", fmt.Errorf(
|
[ADD] cache qemu : messages du binaire en anglais ou en français
Le binaire ne parlait que français alors que le menu suit EL_LANG : un
opérateur anglophone lisait un statut qu'il ne comprenait pas. Chaque message
humain passe par T(), dont la clé EST le texte français, et un catalogue
anglais le traduit ; la langue vient de --lang, puis d'EL_LANG, le français
restant le défaut. Le menu demande le français pour ce qu'il RELIT, ses
motifs ne connaissant que cette langue. Un test refuse un message sans
traduction, une traduction orpheline et un verbe de format qui diverge.
--- EN ---
The binary spoke French only while the menu follows EL_LANG: an English
reader got a status he could not read. Every human message goes through T(),
whose key IS the French text, and an English catalogue translates it; the
language comes from --lang, then EL_LANG, French remaining the default. The
menu asks for French for what it READS BACK, its patterns knowing that
language alone. A test refuses a message without translation, an orphan
translation, and a format verb that drifts.
Assisted-by: Claude Opus 5
2026-09-16 02:59:01 -04:00
|
|
|
T("%s est une adresse de groupe : jamais une adresse source"), adr)
|
[ADD] cache qemu : miroir des téléchargements des VM, hors ligne compris
VMs of one host pulled the same packages again and again, and an install
could not be proven to work without the network. A Go service intercepts the
bridge transparently: a package file is served from disk, an index is always
revalidated upstream and replayed only when upstream is mute, git is mirrored.
Deployment poses the cache authority in QEMU and Proxmox VE guests, and can cut
the network for a whole install. The TODO menu installs, diagnoses, fills,
cleans and copies the store; long_test/qemu_cache.py measures that a second VM
pulls no package upstream. Covered by Go tests and test/test_qemu_cache_*.py.
--- FR ---
Les VM d'un hôte tiraient sans cesse les mêmes paquets, et rien ne prouvait
qu'une installation marche sans réseau. Un service Go intercepte le pont de
façon transparente : un fichier de paquet est servi du disque, un index est
toujours revalidé à l'amont et rejoué seulement quand l'amont est muet, git
est mis en miroir. Le déploiement pose l'autorité du cache dans les invités
QEMU et Proxmox VE, et peut couper le réseau pour toute une installation. Le
menu TODO installe, diagnostique, remplit, nettoie et emporte le magasin ;
long_test/qemu_cache.py mesure qu'une seconde VM ne tire aucun paquet de
l'amont. Couvert par les tests Go et test/test_qemu_cache_*.py.
Assisted-by: Claude Opus 5
2026-09-14 14:41:19 -04:00
|
|
|
}
|
|
|
|
|
nulle := true
|
|
|
|
|
for _, o := range adr {
|
|
|
|
|
if o != 0 {
|
|
|
|
|
nulle = false
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if nulle {
|
[ADD] cache qemu : messages du binaire en anglais ou en français
Le binaire ne parlait que français alors que le menu suit EL_LANG : un
opérateur anglophone lisait un statut qu'il ne comprenait pas. Chaque message
humain passe par T(), dont la clé EST le texte français, et un catalogue
anglais le traduit ; la langue vient de --lang, puis d'EL_LANG, le français
restant le défaut. Le menu demande le français pour ce qu'il RELIT, ses
motifs ne connaissant que cette langue. Un test refuse un message sans
traduction, une traduction orpheline et un verbe de format qui diverge.
--- EN ---
The binary spoke French only while the menu follows EL_LANG: an English
reader got a status he could not read. Every human message goes through T(),
whose key IS the French text, and an English catalogue translates it; the
language comes from --lang, then EL_LANG, French remaining the default. The
menu asks for French for what it READS BACK, its patterns knowing that
language alone. A test refuses a message without translation, an orphan
translation, and a format verb that drifts.
Assisted-by: Claude Opus 5
2026-09-16 02:59:01 -04:00
|
|
|
return "", fmt.Errorf("%s", T("l'adresse nulle n'identifie aucune machine"))
|
[ADD] cache qemu : miroir des téléchargements des VM, hors ligne compris
VMs of one host pulled the same packages again and again, and an install
could not be proven to work without the network. A Go service intercepts the
bridge transparently: a package file is served from disk, an index is always
revalidated upstream and replayed only when upstream is mute, git is mirrored.
Deployment poses the cache authority in QEMU and Proxmox VE guests, and can cut
the network for a whole install. The TODO menu installs, diagnoses, fills,
cleans and copies the store; long_test/qemu_cache.py measures that a second VM
pulls no package upstream. Covered by Go tests and test/test_qemu_cache_*.py.
--- FR ---
Les VM d'un hôte tiraient sans cesse les mêmes paquets, et rien ne prouvait
qu'une installation marche sans réseau. Un service Go intercepte le pont de
façon transparente : un fichier de paquet est servi du disque, un index est
toujours revalidé à l'amont et rejoué seulement quand l'amont est muet, git
est mis en miroir. Le déploiement pose l'autorité du cache dans les invités
QEMU et Proxmox VE, et peut couper le réseau pour toute une installation. Le
menu TODO installe, diagnostique, remplit, nettoie et emporte le magasin ;
long_test/qemu_cache.py mesure qu'une seconde VM ne tire aucun paquet de
l'amont. Couvert par les tests Go et test/test_qemu_cache_*.py.
Assisted-by: Claude Opus 5
2026-09-14 14:41:19 -04:00
|
|
|
}
|
|
|
|
|
return strings.ToLower(adr.String()), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BypassFile est la liste DURABLE. L'ensemble nftables vit dans le noyau et
|
|
|
|
|
// disparaît avec la table ; ce fichier est ce que le service relit à chaque
|
|
|
|
|
// démarrage pour reposer les mêmes exceptions.
|
|
|
|
|
type BypassFile struct {
|
|
|
|
|
Path string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Load rend les entrées, triées par MAC. Un fichier absent n'est pas une
|
|
|
|
|
// erreur : c'est l'état d'une installation où personne n'a rien excepté.
|
|
|
|
|
//
|
|
|
|
|
// Une ligne illisible est SAUTÉE et non fatale. Le fichier est éditable à la
|
|
|
|
|
// main, et refuser de démarrer le service pour une faute de frappe dans une
|
|
|
|
|
// exception coûterait plus que d'ignorer la ligne.
|
|
|
|
|
func (b BypassFile) Load() ([]BypassEntry, error) {
|
|
|
|
|
fh, err := os.Open(b.Path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
|
return nil, nil
|
|
|
|
|
}
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
defer fh.Close()
|
|
|
|
|
|
|
|
|
|
vues := map[string]bool{}
|
|
|
|
|
var out []BypassEntry
|
|
|
|
|
sc := bufio.NewScanner(fh)
|
|
|
|
|
for sc.Scan() {
|
|
|
|
|
ligne := strings.TrimSpace(sc.Text())
|
|
|
|
|
if i := strings.IndexByte(ligne, '#'); i >= 0 {
|
|
|
|
|
ligne = strings.TrimSpace(ligne[:i])
|
|
|
|
|
}
|
|
|
|
|
if ligne == "" {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
champs := strings.Fields(ligne)
|
|
|
|
|
mac, err := NormalizeMAC(champs[0])
|
|
|
|
|
if err != nil || vues[mac] {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
vues[mac] = true
|
|
|
|
|
nom := ""
|
|
|
|
|
if len(champs) > 1 {
|
|
|
|
|
nom = strings.Join(champs[1:], " ")
|
|
|
|
|
}
|
|
|
|
|
out = append(out, BypassEntry{MAC: mac, Name: nom})
|
|
|
|
|
}
|
|
|
|
|
if err := sc.Err(); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
sort.Slice(out, func(i, j int) bool { return out[i].MAC < out[j].MAC })
|
|
|
|
|
return out, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Save réécrit le fichier en entier, par un remplacement atomique : une
|
|
|
|
|
// écriture coupée en deux laisserait une liste tronquée que le prochain
|
|
|
|
|
// démarrage prendrait pour la vérité.
|
|
|
|
|
func (b BypassFile) Save(entries []BypassEntry) error {
|
|
|
|
|
if err := os.MkdirAll(filepath.Dir(b.Path), 0o755); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
var sb strings.Builder
|
|
|
|
|
sb.WriteString("# Exceptions du cache de téléchargement des VM QEMU.\n")
|
|
|
|
|
sb.WriteString("# Une ligne « <MAC> <nom de la VM> » par machine que le\n")
|
|
|
|
|
sb.WriteString("# détournement doit ignorer. Relu à chaque démarrage du\n")
|
|
|
|
|
sb.WriteString("# service ; une MAC dont la VM n'existe plus est à retirer.\n")
|
|
|
|
|
for _, e := range entries {
|
|
|
|
|
if e.Name == "" {
|
|
|
|
|
sb.WriteString(e.MAC + "\n")
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
fmt.Fprintf(&sb, "%s %s\n", e.MAC, e.Name)
|
|
|
|
|
}
|
|
|
|
|
tmp := b.Path + ".part"
|
|
|
|
|
if err := os.WriteFile(tmp, []byte(sb.String()), 0o644); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return os.Rename(tmp, b.Path)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add pose une exception et rend la MAC canonique. Reposer une MAC déjà
|
|
|
|
|
// présente met son nom à jour au lieu de créer un doublon : le déploiement
|
|
|
|
|
// est relancé, et deux lignes pour une machine rendraient le retrait
|
|
|
|
|
// incomplet.
|
|
|
|
|
func (b BypassFile) Add(mac, nom string) (string, error) {
|
|
|
|
|
canon, err := NormalizeMAC(mac)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
entries, err := b.Load()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
remplacee := false
|
|
|
|
|
for i := range entries {
|
|
|
|
|
if entries[i].MAC == canon {
|
|
|
|
|
entries[i].Name = nom
|
|
|
|
|
remplacee = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !remplacee {
|
|
|
|
|
entries = append(entries, BypassEntry{MAC: canon, Name: nom})
|
|
|
|
|
sort.Slice(entries, func(i, j int) bool {
|
|
|
|
|
return entries[i].MAC < entries[j].MAC
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
return canon, b.Save(entries)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Del retire une exception. Rend faux si elle n'y était pas — retirer deux
|
|
|
|
|
// fois n'est pas une erreur, mais l'appelant doit pouvoir le dire.
|
|
|
|
|
func (b BypassFile) Del(mac string) (string, bool, error) {
|
|
|
|
|
canon, err := NormalizeMAC(mac)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", false, err
|
|
|
|
|
}
|
|
|
|
|
entries, err := b.Load()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", false, err
|
|
|
|
|
}
|
|
|
|
|
var reste []BypassEntry
|
|
|
|
|
trouvee := false
|
|
|
|
|
for _, e := range entries {
|
|
|
|
|
if e.MAC == canon {
|
|
|
|
|
trouvee = true
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
reste = append(reste, e)
|
|
|
|
|
}
|
|
|
|
|
if !trouvee {
|
|
|
|
|
return canon, false, nil
|
|
|
|
|
}
|
|
|
|
|
return canon, true, b.Save(reste)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MACs rend les seules adresses, dans l'ordre du fichier.
|
|
|
|
|
func MACs(entries []BypassEntry) []string {
|
|
|
|
|
out := make([]string, 0, len(entries))
|
|
|
|
|
for _, e := range entries {
|
|
|
|
|
out = append(out, e.MAC)
|
|
|
|
|
}
|
|
|
|
|
return out
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BypassAddElement et BypassDelElement rendent le geste À CHAUD, à passer à
|
|
|
|
|
// « nft -f - ». Modifier l'ensemble ne repose pas les règles : les
|
|
|
|
|
// téléchargements des autres VM ne sont pas coupés, ce qu'un redémarrage du
|
|
|
|
|
// service ferait.
|
|
|
|
|
//
|
|
|
|
|
// Comme le reste de ce paquet, ces lignes sont RENDUES et jamais exécutées :
|
|
|
|
|
// l'appelant les applique, et un test les vérifie sans toucher au pare-feu de
|
|
|
|
|
// la machine qui l'exécute.
|
|
|
|
|
func BypassAddElement(mac string) string {
|
|
|
|
|
return fmt.Sprintf("add element ip %s %s { %s }",
|
|
|
|
|
TableName, BypassSetName, mac)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func BypassDelElement(mac string) string {
|
|
|
|
|
return fmt.Sprintf("delete element ip %s %s { %s }",
|
|
|
|
|
TableName, BypassSetName, mac)
|
|
|
|
|
}
|