@@ -54,13 +54,33 @@ options_postprocess_pull(struct options *options, struct env_set *es)
5454 return true;
5555}
5656
57+ /*
58+ * Counters to track route accumulation across continuation messages.
59+ * Used to verify the bug where update_options_found resets per message.
60+ */
61+ static int route_reset_count = 0 ;
62+ static int route_add_count = 0 ;
63+
64+ static void
65+ reset_route_counters (void )
66+ {
67+ route_reset_count = 0 ;
68+ route_add_count = 0 ;
69+ }
70+
5771bool
5872apply_push_options (struct context * c , struct options * options , struct buffer * buf ,
5973 unsigned int permission_mask , unsigned int * option_types_found ,
6074 struct env_set * es , bool is_update )
6175{
6276 char line [OPTION_PARM_SIZE ];
6377
78+ /*
79+ * Use persistent push_update_options_found from options struct to track
80+ * which option types have been reset across continuation messages.
81+ * This is the FIXED behavior - routes are only reset once per PUSH_UPDATE sequence.
82+ */
83+
6484 while (buf_parse (buf , ',' , line , sizeof (line )))
6585 {
6686 unsigned int push_update_option_flags = 0 ;
@@ -84,10 +104,27 @@ apply_push_options(struct context *c, struct options *options, struct buffer *bu
84104 return false; /* Cause push/pull error and stop push processing */
85105 }
86106 }
87- /*
88- * No need to test also the application part here
89- * (add_option/remove_option/update_option)
90- */
107+
108+ /* Simulate route handling from update_option() in options.c */
109+ if (strncmp (& line [i ], "route " , 6 ) == 0 )
110+ {
111+ if (!(options -> push_update_options_found & OPT_P_U_ROUTE ))
112+ {
113+ /* First route in entire PUSH_UPDATE sequence - reset routes once */
114+ route_reset_count ++ ;
115+ options -> push_update_options_found |= OPT_P_U_ROUTE ;
116+ }
117+ route_add_count ++ ;
118+ }
119+ /* Simulate add_option() push-continuation logic */
120+ else if (!strcmp (& line [i ], "push-continuation 2" ))
121+ {
122+ options -> push_continuation = 2 ;
123+ }
124+ else if (!strcmp (& line [i ], "push-continuation 1" ))
125+ {
126+ options -> push_continuation = 1 ;
127+ }
91128 }
92129 return true;
93130}
@@ -292,6 +329,65 @@ test_incoming_push_message_mix2(void **state)
292329 free_buf (& buf );
293330}
294331
332+ /**
333+ * Test that routes accumulate correctly across multiple continuation messages.
334+ * This test exposes a bug where update_options_found is reset to 0 for each
335+ * continuation message, causing routes to be reset on each message instead
336+ * of accumulating.
337+ *
338+ * Expected behavior: routes should only be reset ONCE (when the first route is received),
339+ * then all subsequent routes should accumulate.
340+ *
341+ * Current bug: routes are reset on the first route of EACH continuation message.
342+ */
343+ static void
344+ test_incoming_push_continuation_route_accumulation (void * * state )
345+ {
346+ struct context * c = * state ;
347+ unsigned int option_types_found = 0 ;
348+
349+ reset_route_counters ();
350+
351+ /* Message 1: first batch of routes, continuation 2 (more coming) */
352+ struct buffer buf1 = alloc_buf (512 );
353+ const char * msg1 = "PUSH_UPDATE, route 10.1.0.0 255.255.0.0, route 10.2.0.0 255.255.0.0, route 10.3.0.0 255.255.0.0,push-continuation 2" ;
354+ buf_write (& buf1 , msg1 , strlen (msg1 ));
355+
356+ assert_int_equal (process_incoming_push_msg (c , & buf1 , c -> options .pull , pull_permission_mask (c ),
357+ & option_types_found ),
358+ PUSH_MSG_CONTINUATION );
359+ free_buf (& buf1 );
360+
361+ /* Message 2: more routes, continuation 2 (more coming) */
362+ struct buffer buf2 = alloc_buf (512 );
363+ const char * msg2 = "PUSH_UPDATE, route 10.4.0.0 255.255.0.0, route 10.5.0.0 255.255.0.0, route 10.6.0.0 255.255.0.0,push-continuation 2" ;
364+ buf_write (& buf2 , msg2 , strlen (msg2 ));
365+
366+ assert_int_equal (process_incoming_push_msg (c , & buf2 , c -> options .pull , pull_permission_mask (c ),
367+ & option_types_found ),
368+ PUSH_MSG_CONTINUATION );
369+ free_buf (& buf2 );
370+
371+ /* Message 3: final batch of routes, continuation 1 (last message) */
372+ struct buffer buf3 = alloc_buf (512 );
373+ const char * msg3 = "PUSH_UPDATE, route 10.7.0.0 255.255.0.0, route 10.8.0.0 255.255.0.0, route 10.9.0.0 255.255.0.0,push-continuation 1" ;
374+ buf_write (& buf3 , msg3 , strlen (msg3 ));
375+
376+ assert_int_equal (process_incoming_push_msg (c , & buf3 , c -> options .pull , pull_permission_mask (c ),
377+ & option_types_found ),
378+ PUSH_MSG_UPDATE );
379+ free_buf (& buf3 );
380+
381+ /* Verify: all 9 routes should have been added */
382+ assert_int_equal (route_add_count , 9 );
383+
384+ /*
385+ * Verify: route option is reset only one time in the first message
386+ * if a push-continuation is present.
387+ */
388+ assert_int_equal (route_reset_count , 1 );
389+ }
390+
295391#ifdef ENABLE_MANAGEMENT
296392char * r0 [] = {
297393 "PUSH_UPDATE,redirect-gateway local,route 192.168.1.0 255.255.255.0" ,
@@ -603,6 +699,8 @@ main(void)
603699 cmocka_unit_test_setup_teardown (test_incoming_push_message_bad_format , setup , teardown ),
604700 cmocka_unit_test_setup_teardown (test_incoming_push_message_mix , setup , teardown ),
605701 cmocka_unit_test_setup_teardown (test_incoming_push_message_mix2 , setup , teardown ),
702+ cmocka_unit_test_setup_teardown (test_incoming_push_continuation_route_accumulation , setup ,
703+ teardown ),
606704#ifdef ENABLE_MANAGEMENT
607705
608706 cmocka_unit_test_setup_teardown (test_send_push_msg0 , setup2 , teardown2 ),
0 commit comments