Skip to content

Commit 6d44146

Browse files
committed
Delay port redirect until packet reaches container
With port redirect in the ingress path happening before ipvs in the ingess sandbox, there is a chance of 5-tuple collision in the ipvs connection table for two entirely different services have different PublishedPorts but the same TargetPort. To disambiguate the ipvs connection table, delay the port redirect from PublishedPort to TargetPort until after the loadbalancing has happened in ipvs. To be specific, perform the redirect after the packet enters the real backend container namespace. Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
1 parent ab555e2 commit 6d44146

1 file changed

Lines changed: 95 additions & 34 deletions

File tree

service_linux.go

Lines changed: 95 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626

2727
func init() {
2828
reexec.Register("fwmarker", fwMarker)
29+
reexec.Register("redirecter", redirecter)
2930
}
3031

3132
func newService(name string, id string, ingressPorts []*PortConfig, aliases []string) *service {
@@ -275,6 +276,12 @@ func (sb *sandbox) populateLoadbalancers(ep *endpoint) {
275276
n := ep.getNetwork()
276277
eIP := ep.Iface().Address()
277278

279+
if n.ingress {
280+
if err := addRedirectRules(sb.Key(), eIP, ep.ingressPorts); err != nil {
281+
logrus.Errorf("Failed to add redirect rules for ep %s: %v", ep.Name(), err)
282+
}
283+
}
284+
278285
if sb.ingress {
279286
// For the ingress sandbox if this is not gateway
280287
// endpoint do nothing.
@@ -390,7 +397,7 @@ func (sb *sandbox) addLBBackend(ip, vip net.IP, fwMark uint32, ingressPorts []*P
390397
}
391398

392399
logrus.Debugf("Creating service for vip %s fwMark %d ingressPorts %#v", vip, fwMark, ingressPorts)
393-
if err := invokeFWMarker(sb.Key(), vip, fwMark, ingressPorts, filteredPorts, eIP, false); err != nil {
400+
if err := invokeFWMarker(sb.Key(), vip, fwMark, ingressPorts, eIP, false); err != nil {
394401
logrus.Errorf("Failed to add firewall mark rule in sbox %s: %v", sb.Key(), err)
395402
return
396403
}
@@ -461,7 +468,7 @@ func (sb *sandbox) rmLBBackend(ip, vip net.IP, fwMark uint32, ingressPorts []*Po
461468
}
462469
}
463470

464-
if err := invokeFWMarker(sb.Key(), vip, fwMark, ingressPorts, filteredPorts, eIP, true); err != nil {
471+
if err := invokeFWMarker(sb.Key(), vip, fwMark, ingressPorts, eIP, true); err != nil {
465472
logrus.Errorf("Failed to add firewall mark rule in sbox %s: %v", sb.Key(), err)
466473
}
467474
}
@@ -755,26 +762,17 @@ func readPortsFromFile(fileName string) ([]*PortConfig, error) {
755762

756763
// Invoke fwmarker reexec routine to mark vip destined packets with
757764
// the passed firewall mark.
758-
func invokeFWMarker(path string, vip net.IP, fwMark uint32, ingressPorts []*PortConfig, filteredPorts []*PortConfig, eIP *net.IPNet, isDelete bool) error {
759-
var (
760-
ingressPortsFile string
761-
filteredPortsFile string
762-
)
765+
func invokeFWMarker(path string, vip net.IP, fwMark uint32, ingressPorts []*PortConfig, eIP *net.IPNet, isDelete bool) error {
766+
var ingressPortsFile string
763767

764768
if len(ingressPorts) != 0 {
765769
var err error
766770
ingressPortsFile, err = writePortsToFile(ingressPorts)
767771
if err != nil {
768772
return err
769773
}
770-
}
771774

772-
if len(filteredPorts) != 0 {
773-
var err error
774-
filteredPortsFile, err = writePortsToFile(filteredPorts)
775-
if err != nil {
776-
return err
777-
}
775+
defer os.Remove(ingressPortsFile)
778776
}
779777

780778
addDelOpt := "-A"
@@ -784,7 +782,7 @@ func invokeFWMarker(path string, vip net.IP, fwMark uint32, ingressPorts []*Port
784782

785783
cmd := &exec.Cmd{
786784
Path: reexec.Self(),
787-
Args: append([]string{"fwmarker"}, path, vip.String(), fmt.Sprintf("%d", fwMark), addDelOpt, ingressPortsFile, filteredPortsFile, eIP.String()),
785+
Args: append([]string{"fwmarker"}, path, vip.String(), fmt.Sprintf("%d", fwMark), addDelOpt, ingressPortsFile, eIP.String()),
788786
Stdout: os.Stdout,
789787
Stderr: os.Stderr,
790788
}
@@ -801,13 +799,12 @@ func fwMarker() {
801799
runtime.LockOSThread()
802800
defer runtime.UnlockOSThread()
803801

804-
if len(os.Args) < 8 {
802+
if len(os.Args) < 7 {
805803
logrus.Error("invalid number of arguments..")
806804
os.Exit(1)
807805
}
808806

809807
var ingressPorts []*PortConfig
810-
var filteredPorts []*PortConfig
811808
if os.Args[5] != "" {
812809
var err error
813810
ingressPorts, err = readPortsFromFile(os.Args[5])
@@ -817,15 +814,6 @@ func fwMarker() {
817814
}
818815
}
819816

820-
if os.Args[6] != "" {
821-
var err error
822-
filteredPorts, err = readPortsFromFile(os.Args[6])
823-
if err != nil {
824-
logrus.Errorf("Failed reading filtered ports file: %v", err)
825-
os.Exit(7)
826-
}
827-
}
828-
829817
vip := os.Args[2]
830818
fwMark, err := strconv.ParseUint(os.Args[3], 10, 32)
831819
if err != nil {
@@ -835,12 +823,6 @@ func fwMarker() {
835823
addDelOpt := os.Args[4]
836824

837825
rules := [][]string{}
838-
for _, iPort := range filteredPorts {
839-
rule := strings.Fields(fmt.Sprintf("-t nat %s PREROUTING -p %s --dport %d -j REDIRECT --to-port %d",
840-
addDelOpt, strings.ToLower(PortConfig_Protocol_name[int32(iPort.Protocol)]), iPort.PublishedPort, iPort.TargetPort))
841-
rules = append(rules, rule)
842-
}
843-
844826
for _, iPort := range ingressPorts {
845827
rule := strings.Fields(fmt.Sprintf("-t mangle %s PREROUTING -p %s --dport %d -j MARK --set-mark %d",
846828
addDelOpt, strings.ToLower(PortConfig_Protocol_name[int32(iPort.Protocol)]), iPort.PublishedPort, fwMark))
@@ -860,9 +842,9 @@ func fwMarker() {
860842
}
861843

862844
if addDelOpt == "-A" {
863-
eIP, subnet, err := net.ParseCIDR(os.Args[7])
845+
eIP, subnet, err := net.ParseCIDR(os.Args[6])
864846
if err != nil {
865-
logrus.Errorf("Failed to parse endpoint IP %s: %v", os.Args[7], err)
847+
logrus.Errorf("Failed to parse endpoint IP %s: %v", os.Args[6], err)
866848
os.Exit(9)
867849
}
868850

@@ -889,3 +871,82 @@ func fwMarker() {
889871
}
890872
}
891873
}
874+
875+
func addRedirectRules(path string, eIP *net.IPNet, ingressPorts []*PortConfig) error {
876+
var ingressPortsFile string
877+
878+
if len(ingressPorts) != 0 {
879+
var err error
880+
ingressPortsFile, err = writePortsToFile(ingressPorts)
881+
if err != nil {
882+
return err
883+
}
884+
defer os.Remove(ingressPortsFile)
885+
}
886+
887+
cmd := &exec.Cmd{
888+
Path: reexec.Self(),
889+
Args: append([]string{"redirecter"}, path, eIP.String(), ingressPortsFile),
890+
Stdout: os.Stdout,
891+
Stderr: os.Stderr,
892+
}
893+
894+
if err := cmd.Run(); err != nil {
895+
return fmt.Errorf("reexec failed: %v", err)
896+
}
897+
898+
return nil
899+
}
900+
901+
// Redirecter reexec function.
902+
func redirecter() {
903+
runtime.LockOSThread()
904+
defer runtime.UnlockOSThread()
905+
906+
if len(os.Args) < 4 {
907+
logrus.Error("invalid number of arguments..")
908+
os.Exit(1)
909+
}
910+
911+
var ingressPorts []*PortConfig
912+
if os.Args[3] != "" {
913+
var err error
914+
ingressPorts, err = readPortsFromFile(os.Args[3])
915+
if err != nil {
916+
logrus.Errorf("Failed reading ingress ports file: %v", err)
917+
os.Exit(2)
918+
}
919+
}
920+
921+
eIP, _, err := net.ParseCIDR(os.Args[2])
922+
if err != nil {
923+
logrus.Errorf("Failed to parse endpoint IP %s: %v", os.Args[2], err)
924+
os.Exit(3)
925+
}
926+
927+
rules := [][]string{}
928+
for _, iPort := range ingressPorts {
929+
rule := strings.Fields(fmt.Sprintf("-t nat -A PREROUTING -d %s -p %s --dport %d -j REDIRECT --to-port %d",
930+
eIP.String(), strings.ToLower(PortConfig_Protocol_name[int32(iPort.Protocol)]), iPort.PublishedPort, iPort.TargetPort))
931+
rules = append(rules, rule)
932+
}
933+
934+
ns, err := netns.GetFromPath(os.Args[1])
935+
if err != nil {
936+
logrus.Errorf("failed get network namespace %q: %v", os.Args[1], err)
937+
os.Exit(4)
938+
}
939+
defer ns.Close()
940+
941+
if err := netns.Set(ns); err != nil {
942+
logrus.Errorf("setting into container net ns %v failed, %v", os.Args[1], err)
943+
os.Exit(5)
944+
}
945+
946+
for _, rule := range rules {
947+
if err := iptables.RawCombinedOutputNative(rule...); err != nil {
948+
logrus.Errorf("setting up rule failed, %v: %v", rule, err)
949+
os.Exit(5)
950+
}
951+
}
952+
}

0 commit comments

Comments
 (0)