@@ -30,21 +30,27 @@ type RawConner interface {
3030}
3131
3232type PIMServer struct {
33- iface string
34- groups []net.IP
35- done chan struct {}
36- conn RawConner
37- wg * sync.WaitGroup
33+ iface string
34+ groups []net.IP
35+ done chan struct {}
36+ conn RawConner
37+ wg * sync.WaitGroup
38+ tunnelAddr net.IP
39+ updateCh chan []net.IP
3840}
3941
4042func NewPIMServer () * PIMServer {
41- return & PIMServer {done : make (chan struct {})}
43+ return & PIMServer {
44+ done : make (chan struct {}),
45+ updateCh : make (chan []net.IP ),
46+ }
4247}
4348
4449func (s * PIMServer ) Start (conn RawConner , iface string , tunnelAddr net.IP , groups []net.IP ) error {
4550 s .iface = iface
4651 s .groups = groups
4752 s .conn = conn
53+ s .tunnelAddr = tunnelAddr
4854
4955 intf , err := net .InterfaceByName (s .iface )
5056 if err != nil {
@@ -58,61 +64,96 @@ func (s *PIMServer) Start(conn RawConner, iface string, tunnelAddr net.IP, group
5864 s .wg .Add (1 )
5965 go func () {
6066 defer s .conn .Close ()
67+ defer s .wg .Done ()
68+
6169 // send before we start ticker so we don't delay provisioning time by ticker interval
62- helloMsgBuf , err := constructHelloMessage ()
63- if err != nil {
64- slog .Error ("failed to serialize PIM hello msg" , "error" , err )
65- }
66- err = sendMsg (helloMsgBuf , intf , s .conn )
67- if err != nil {
68- slog .Error ("failed to send PIM hello msg" , "error" , err )
69- }
70- joinPruneMsgBuf , err := constructJoinPruneMessage (tunnelAddr , groups , RpAddress , nil , joinHoldtime )
71- if err != nil {
72- slog .Error ("failed to serialize PIM join msg" , "error" , err )
73- }
74- err = sendMsg (joinPruneMsgBuf , intf , s .conn )
75- if err != nil {
76- slog .Error ("failed to send PIM join msg" , "error" , err )
77- }
70+ sendHelloAndJoin (intf , s .conn , tunnelAddr , s .groups )
7871
7972 ticker := time .NewTicker (time .Second * 30 )
73+ defer ticker .Stop ()
8074 for {
8175 select {
8276 case <- ticker .C :
83- helloMsgBuf , err := constructHelloMessage ()
84- if err != nil {
85- slog .Error ("failed to serialize PIM hello msg" , "error" , err )
86- }
87- err = sendMsg (helloMsgBuf , intf , s .conn )
88- if err != nil {
89- slog .Error ("failed to send PIM hello msg" , "error" , err )
90- }
91- joinPruneMsgBuf , err := constructJoinPruneMessage (tunnelAddr , groups , RpAddress , nil , joinHoldtime )
92- if err != nil {
93- slog .Error ("failed to serialize PIM join msg" , "error" , err )
77+ sendHelloAndJoin (intf , s .conn , tunnelAddr , s .groups )
78+ case newGroups := <- s .updateCh :
79+ added , removed := ipDiff (s .groups , newGroups )
80+ if len (removed ) > 0 {
81+ buf , err := constructJoinPruneMessage (tunnelAddr , removed , nil , RpAddress , pruneHoldtime )
82+ if err != nil {
83+ slog .Error ("failed to serialize PIM prune msg for removed groups" , "error" , err )
84+ } else if err := sendMsg (buf , intf , s .conn ); err != nil {
85+ slog .Error ("failed to send PIM prune msg for removed groups" , "error" , err )
86+ }
9487 }
95- err = sendMsg (joinPruneMsgBuf , intf , s .conn )
96- if err != nil {
97- slog .Error ("failed to send PIM join msg" , "error" , err )
88+ if len (added ) > 0 {
89+ buf , err := constructJoinPruneMessage (tunnelAddr , added , RpAddress , nil , joinHoldtime )
90+ if err != nil {
91+ slog .Error ("failed to serialize PIM join msg for added groups" , "error" , err )
92+ } else if err := sendMsg (buf , intf , s .conn ); err != nil {
93+ slog .Error ("failed to send PIM join msg for added groups" , "error" , err )
94+ }
9895 }
96+ s .groups = newGroups
9997 case <- s .done :
100- joinPruneMsgBuf , err := constructJoinPruneMessage (tunnelAddr , groups , nil , RpAddress , pruneHoldtime )
98+ joinPruneMsgBuf , err := constructJoinPruneMessage (tunnelAddr , s . groups , nil , RpAddress , pruneHoldtime )
10199 if err != nil {
102100 slog .Error ("failed to serialize PIM prune msg" , "error" , err )
103- }
104- err = sendMsg (joinPruneMsgBuf , intf , s .conn )
105- if err != nil {
101+ } else if err := sendMsg (joinPruneMsgBuf , intf , s .conn ); err != nil {
106102 slog .Error ("failed to send PIM prune msg" , "error" , err )
107103 }
108- s .wg .Done ()
109104 return
110105 }
111106 }
112107 }()
113108 return nil
114109}
115110
111+ // UpdateGroups applies a new set of multicast groups in-place without
112+ // restarting the goroutine or connection. The goroutine computes the diff
113+ // and sends targeted join/prune messages for added/removed groups.
114+ func (s * PIMServer ) UpdateGroups (groups []net.IP ) error {
115+ s .updateCh <- groups
116+ return nil
117+ }
118+
119+ func sendHelloAndJoin (intf * net.Interface , conn RawConner , tunnelAddr net.IP , groups []net.IP ) {
120+ helloMsgBuf , err := constructHelloMessage ()
121+ if err != nil {
122+ slog .Error ("failed to serialize PIM hello msg" , "error" , err )
123+ } else if err := sendMsg (helloMsgBuf , intf , conn ); err != nil {
124+ slog .Error ("failed to send PIM hello msg" , "error" , err )
125+ }
126+ joinPruneMsgBuf , err := constructJoinPruneMessage (tunnelAddr , groups , RpAddress , nil , joinHoldtime )
127+ if err != nil {
128+ slog .Error ("failed to serialize PIM join msg" , "error" , err )
129+ } else if err := sendMsg (joinPruneMsgBuf , intf , conn ); err != nil {
130+ slog .Error ("failed to send PIM join msg" , "error" , err )
131+ }
132+ }
133+
134+ // ipDiff returns IPs that were added and removed when transitioning from old to new.
135+ func ipDiff (old , new []net.IP ) (added , removed []net.IP ) {
136+ oldSet := make (map [string ]bool , len (old ))
137+ for _ , ip := range old {
138+ oldSet [ip .String ()] = true
139+ }
140+ newSet := make (map [string ]bool , len (new ))
141+ for _ , ip := range new {
142+ newSet [ip .String ()] = true
143+ }
144+ for _ , ip := range new {
145+ if ! oldSet [ip .String ()] {
146+ added = append (added , ip )
147+ }
148+ }
149+ for _ , ip := range old {
150+ if ! newSet [ip .String ()] {
151+ removed = append (removed , ip )
152+ }
153+ }
154+ return
155+ }
156+
116157func (s * PIMServer ) Close () error {
117158 s .done <- struct {}{}
118159 s .wg .Wait ()
0 commit comments