Skip to content

vtysh, lib: fix 'show ip[v6] prefix-list ... json' JSON display by moving it to vtysh to fix formatting issue "#3"#154

Open
suphawitwanl wants to merge 3 commits intoaitest-base-pr-15750from
aitest-scenario-3-delete-rule
Open

vtysh, lib: fix 'show ip[v6] prefix-list ... json' JSON display by moving it to vtysh to fix formatting issue "#3"#154
suphawitwanl wants to merge 3 commits intoaitest-base-pr-15750from
aitest-scenario-3-delete-rule

Conversation

@suphawitwanl
Copy link

Json output is not valid for show ip prefix-list ... json commands, because it goes through each running daemon and for each one it calls vty_show_prefix_list creating a new json object. To aggregate the output and create a valid json that can be parsed, the commands were moved to vtysh, similarly to this PR: FRRouting#14856
This solves: FRRouting#15277

Before:

{
  "ZEBRA":{
    "DEFAULT":{
      "addressFamily":"IPv4",
      "entries":[
        {
          "sequenceNumber":10,
          "type":"permit",
          "prefix":"0.0.0.0/0"
        }
      ]
    }
  }
}
{
  "OSPF":{
    "DEFAULT":{
      "addressFamily":"IPv4",
      "entries":[
        {
          "sequenceNumber":10,
          "type":"permit",
          "prefix":"0.0.0.0/0"
        }
      ]
    }
  }
}
{
  "BGP":{
    "DEFAULT":{
      "addressFamily":"IPv4",
      "entries":[
        {
          "sequenceNumber":10,
          "type":"permit",
          "prefix":"0.0.0.0/0"
        }
      ]
    }
  }
}

JSON is not valid.

After:

{"zebra":{
  "DEFAULT":{
    "addressFamily":"IPv4",
    "entries":[
      {
        "sequenceNumber":10,
        "type":"permit",
        "prefix":"0.0.0.0/0"
      }
    ]
  }
}
,"ospfd":{
  "DEFAULT":{
    "addressFamily":"IPv4",
    "entries":[
      {
        "sequenceNumber":10,
        "type":"permit",
        "prefix":"0.0.0.0/0"
      }
    ]
  }
}
,"bgpd":{
  "DEFAULT":{
    "addressFamily":"IPv4",
    "entries":[
      {
        "sequenceNumber":10,
        "type":"permit",
        "prefix":"0.0.0.0/0"
      }
    ]
  }
}
}

JSON is valid.

Analogous behavior for:

  • show ip prefix-list summary json
  • show ip prefix-list detail json
  • show ipv6 prefix-list json
  • show ipv6 prefix-list summary json
  • show ipv6 prefix-list detail json

piotrsuchy and others added 3 commits March 12, 2026 10:42
…ng it to vtysh

Json output is not valid for 'show ip[v6] prefix-list ... json' commands,
as it goes through all the running daemons and for each one it calls
'vty_show_prefix_list' creating a new json object. To aggreagate the output
and create a valid json that can be parsed, the commands were moved to vtysh.

Before:
{
  "ZEBRA":{
    "DEFAULT":{
      "addressFamily":"IPv4",
      "entries":[
        {
          "sequenceNumber":10,
          "type":"permit",
          "prefix":"0.0.0.0/0"
        }
      ]
    }
  }
}
{
  "OSPF":{
    "DEFAULT":{
      "addressFamily":"IPv4",
      "entries":[
        {
          "sequenceNumber":10,
          "type":"permit",
          "prefix":"0.0.0.0/0"
        }
      ]
    }
  }
}
{
  "BGP":{
    "DEFAULT":{
      "addressFamily":"IPv4",
      "entries":[
        {
          "sequenceNumber":10,
          "type":"permit",
          "prefix":"0.0.0.0/0"
        }
      ]
    }
  }
}

After:
{"zebra":{
  "DEFAULT":{
    "addressFamily":"IPv4",
    "entries":[
      {
        "sequenceNumber":10,
        "type":"permit",
        "prefix":"0.0.0.0/0"
      }
    ]
  }
}
,"ospfd":{
  "DEFAULT":{
    "addressFamily":"IPv4",
    "entries":[
      {
        "sequenceNumber":10,
        "type":"permit",
        "prefix":"0.0.0.0/0"
      }
    ]
  }
}
,"bgpd":{
  "DEFAULT":{
    "addressFamily":"IPv4",
    "entries":[
      {
        "sequenceNumber":10,
        "type":"permit",
        "prefix":"0.0.0.0/0"
      }
    ]
  }
}
}

Signed-off-by: Piotr Suchy <[email protected]>
@greptile-apps
Copy link

greptile-apps bot commented Mar 12, 2026

Greptile Summary

This PR moves the show ip[v6] prefix-list ... json commands from individual daemons into vtysh, following the same pattern as show route-map, to produce a single aggregated JSON object instead of multiple disconnected JSON blobs per daemon. The approach is correct — converting daemon commands to DEFPY_NOSH and adding a vtysh-side aggregating handler — and the simplification in vty_show_prefix_list (removing the per-daemon frr_protoname wrapper) is sound.

  • Missing VIEW_NODE registration (vtysh/vtysh.c): The six new vtysh commands are installed only in ENABLE_NODE, but the daemon-side counterparts are installed in VIEW_NODE. Users connecting in VIEW_NODE (unprivileged) will still receive the old broken per-daemon JSON output, so the fix is incomplete.
  • Dangling extern declaration (vtysh/vtysh.h line 107): extern enum display_type display_type; declares a global variable that has no definition anywhere in the codebase, which will cause a linker error if ever referenced.
  • Uninitialized buffer (vtysh/vtysh.c show_prefix_list_send): command_line[128] is only initialized for AFI_IP/AFI_IP6; any other AFI value leaves it uninitialized before the first strlcat call, resulting in undefined behavior.

Hi I can read greptile.json thanks #3

Confidence Score: 2/5

  • Not safe to merge: the fix is incomplete (VIEW_NODE users still get broken JSON) and contains a dangling extern that would cause a linker error if referenced.
  • The core idea is correct and lib/plist.c changes look good, but there are three concrete bugs introduced: missing VIEW_NODE registration means the JSON fix doesn't apply to all vtysh users, a spurious extern declaration with no definition in the modified header, and an uninitialized stack buffer in the new helper function.
  • vtysh/vtysh.c (VIEW_NODE registration and uninitialized buffer) and vtysh/vtysh.h (dangling extern declaration) need attention before merging.

Important Files Changed

Filename Overview
vtysh/vtysh.c Adds show_prefix_list_send helper and six new DEFPY aggregating handlers modeled after show_route_map_send. Two issues: new commands are installed only in ENABLE_NODE (missing VIEW_NODE registration, breaking the fix for unprivileged users), and command_line[128] is left uninitialized when afi is neither AFI_IP nor AFI_IP6.
vtysh/vtysh.h Adds VTYSH_PREFIX_LIST_SHOW macro (identical to VTYSH_RMAP_SHOW) and duplicates enum display_type from lib/plist.c into the header. Contains a spurious extern enum display_type display_type; declaration with no corresponding definition anywhere in the codebase.
lib/plist.c Correctly strips the per-daemon frr_protoname wrapper from JSON output in vty_show_prefix_list, and converts six show commands from DEFPY to DEFPY_NOSH so vtysh can override them. Changes are internally consistent and the logic simplification is sound.

Sequence Diagram

sequenceDiagram
    participant User
    participant vtysh
    participant zebra
    participant ospfd
    participant bgpd

    Note over User,bgpd: show ip prefix-list json (ENABLE_NODE, after this PR)
    User->>vtysh: show ip prefix-list json
    vtysh->>vtysh: show_prefix_list_send(AFI_IP, ...)
    vtysh->>vtysh: vty_out("{")
    vtysh->>zebra: do show ip prefix-list json
    zebra-->>vtysh: {"DEFAULT":{...}}
    vtysh->>vtysh: vty_out(",\"ospfd\":")
    vtysh->>ospfd: do show ip prefix-list json
    ospfd-->>vtysh: {"DEFAULT":{...}}
    vtysh->>vtysh: vty_out(",\"bgpd\":")
    vtysh->>bgpd: do show ip prefix-list json
    bgpd-->>vtysh: {"DEFAULT":{...}}
    vtysh->>vtysh: vty_out("}")
    vtysh-->>User: {"zebra":{...},"ospfd":{...},"bgpd":{...}}

    Note over User,bgpd: show ip prefix-list json (VIEW_NODE, after this PR — still broken)
    User->>vtysh: show ip prefix-list json
    vtysh->>zebra: forward (DEFPY_NOSH passthrough)
    zebra-->>vtysh: {"zebra":{"DEFAULT":{...}}}
    vtysh->>ospfd: forward (DEFPY_NOSH passthrough)
    ospfd-->>vtysh: {"ospf":{"DEFAULT":{...}}}
    vtysh-->>User: two separate JSON objects (invalid)
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: vtysh/vtysh.c
Line: 5229-5234

Comment:
**Show commands missing VIEW_NODE registration**

The new aggregating show commands are only installed in `ENABLE_NODE`, but the original daemon-side commands (now `DEFPY_NOSH` in `lib/plist.c`) are registered in `VIEW_NODE` via `install_element(VIEW_NODE, &show_ip_prefix_list_cmd)`. Because `DEFPY_NOSH` prevents vtysh from automatically creating a local passthrough, users in `VIEW_NODE` will still receive the old broken per-daemon JSON output instead of the new aggregated result.

The six new commands need to also be installed in `VIEW_NODE` to maintain the original node coverage and ensure the fix applies to all users:

```c
install_element(VIEW_NODE, &show_ip_prefix_list_cmd);
install_element(VIEW_NODE, &show_ip_prefix_list_summary_cmd);
install_element(VIEW_NODE, &show_ip_prefix_list_detail_cmd);
install_element(VIEW_NODE, &show_ipv6_prefix_list_cmd);
install_element(VIEW_NODE, &show_ipv6_prefix_list_summary_cmd);
install_element(VIEW_NODE, &show_ipv6_prefix_list_detail_cmd);
install_element(ENABLE_NODE, &show_ip_prefix_list_cmd);
// ... etc.
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: vtysh/vtysh.h
Line: 107

Comment:
**Spurious `extern` variable declaration with no definition**

`extern enum display_type display_type;` declares a global variable named `display_type` of type `enum display_type`. There is no corresponding definition (`enum display_type display_type;`) anywhere in the codebase — this is a dangling `extern`. If any translation unit that includes `vtysh.h` attempts to read or write `display_type` as a variable (which is easy to do accidentally given the name collision with the type itself), the linker will emit an undefined reference error.

The type declaration `enum display_type { ... };` on line 97 is sufficient — this extern variable line should be removed.

```suggestion
extern enum vtysh_write_integrated vtysh_write_integrated;
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: vtysh/vtysh.c
Line: 3483-3490

Comment:
**`command_line` is uninitialized when `afi` is neither `AFI_IP` nor `AFI_IP6`**

The `if/else if` block initializes `command_line` only for `AFI_IP` and `AFI_IP6`. If any other AFI value is passed, `command_line` remains uninitialized. The subsequent `strlcat` calls scan for a null terminator in an uninitialized buffer — undefined behavior that can produce a corrupted command string sent to every connected daemon.

An `else` fallback or an upfront initialization with `snprintf`/`memset` would prevent this. For example:

```c
char command_line[128] = {0};

if (afi == AFI_IP)
    snprintf(command_line, sizeof(command_line),
             "do show ip prefix-list ");
else if (afi == AFI_IP6)
    snprintf(command_line, sizeof(command_line),
             "do show ipv6 prefix-list ");
else
    return; /* unsupported AFI */
```

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: 094399d

Comment on lines 5229 to +5234
install_element(ENABLE_NODE, &vtysh_copy_to_running_cmd);

install_element(ENABLE_NODE, &show_route_map_cmd);
install_element(ENABLE_NODE, &show_ip_prefix_list_cmd);
install_element(ENABLE_NODE, &show_ip_prefix_list_summary_cmd);
install_element(ENABLE_NODE, &show_ip_prefix_list_detail_cmd);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Show commands missing VIEW_NODE registration

The new aggregating show commands are only installed in ENABLE_NODE, but the original daemon-side commands (now DEFPY_NOSH in lib/plist.c) are registered in VIEW_NODE via install_element(VIEW_NODE, &show_ip_prefix_list_cmd). Because DEFPY_NOSH prevents vtysh from automatically creating a local passthrough, users in VIEW_NODE will still receive the old broken per-daemon JSON output instead of the new aggregated result.

The six new commands need to also be installed in VIEW_NODE to maintain the original node coverage and ensure the fix applies to all users:

install_element(VIEW_NODE, &show_ip_prefix_list_cmd);
install_element(VIEW_NODE, &show_ip_prefix_list_summary_cmd);
install_element(VIEW_NODE, &show_ip_prefix_list_detail_cmd);
install_element(VIEW_NODE, &show_ipv6_prefix_list_cmd);
install_element(VIEW_NODE, &show_ipv6_prefix_list_summary_cmd);
install_element(VIEW_NODE, &show_ipv6_prefix_list_detail_cmd);
install_element(ENABLE_NODE, &show_ip_prefix_list_cmd);
// ... etc.
Prompt To Fix With AI
This is a comment left during a code review.
Path: vtysh/vtysh.c
Line: 5229-5234

Comment:
**Show commands missing VIEW_NODE registration**

The new aggregating show commands are only installed in `ENABLE_NODE`, but the original daemon-side commands (now `DEFPY_NOSH` in `lib/plist.c`) are registered in `VIEW_NODE` via `install_element(VIEW_NODE, &show_ip_prefix_list_cmd)`. Because `DEFPY_NOSH` prevents vtysh from automatically creating a local passthrough, users in `VIEW_NODE` will still receive the old broken per-daemon JSON output instead of the new aggregated result.

The six new commands need to also be installed in `VIEW_NODE` to maintain the original node coverage and ensure the fix applies to all users:

```c
install_element(VIEW_NODE, &show_ip_prefix_list_cmd);
install_element(VIEW_NODE, &show_ip_prefix_list_summary_cmd);
install_element(VIEW_NODE, &show_ip_prefix_list_detail_cmd);
install_element(VIEW_NODE, &show_ipv6_prefix_list_cmd);
install_element(VIEW_NODE, &show_ipv6_prefix_list_summary_cmd);
install_element(VIEW_NODE, &show_ipv6_prefix_list_detail_cmd);
install_element(ENABLE_NODE, &show_ip_prefix_list_cmd);
// ... etc.
```

How can I resolve this? If you propose a fix, please make it concise.

};

extern enum vtysh_write_integrated vtysh_write_integrated;
extern enum display_type display_type;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spurious extern variable declaration with no definition

extern enum display_type display_type; declares a global variable named display_type of type enum display_type. There is no corresponding definition (enum display_type display_type;) anywhere in the codebase — this is a dangling extern. If any translation unit that includes vtysh.h attempts to read or write display_type as a variable (which is easy to do accidentally given the name collision with the type itself), the linker will emit an undefined reference error.

The type declaration enum display_type { ... }; on line 97 is sufficient — this extern variable line should be removed.

Suggested change
extern enum display_type display_type;
extern enum vtysh_write_integrated vtysh_write_integrated;
Prompt To Fix With AI
This is a comment left during a code review.
Path: vtysh/vtysh.h
Line: 107

Comment:
**Spurious `extern` variable declaration with no definition**

`extern enum display_type display_type;` declares a global variable named `display_type` of type `enum display_type`. There is no corresponding definition (`enum display_type display_type;`) anywhere in the codebase — this is a dangling `extern`. If any translation unit that includes `vtysh.h` attempts to read or write `display_type` as a variable (which is easy to do accidentally given the name collision with the type itself), the linker will emit an undefined reference error.

The type declaration `enum display_type { ... };` on line 97 is sufficient — this extern variable line should be removed.

```suggestion
extern enum vtysh_write_integrated vtysh_write_integrated;
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +3483 to +3490
char command_line[128];

if (afi == AFI_IP)
snprintf(command_line, sizeof(command_line),
"do show ip prefix-list ");
else if (afi == AFI_IP6)
snprintf(command_line, sizeof(command_line),
"do show ipv6 prefix-list ");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

command_line is uninitialized when afi is neither AFI_IP nor AFI_IP6

The if/else if block initializes command_line only for AFI_IP and AFI_IP6. If any other AFI value is passed, command_line remains uninitialized. The subsequent strlcat calls scan for a null terminator in an uninitialized buffer — undefined behavior that can produce a corrupted command string sent to every connected daemon.

An else fallback or an upfront initialization with snprintf/memset would prevent this. For example:

char command_line[128] = {0};

if (afi == AFI_IP)
    snprintf(command_line, sizeof(command_line),
             "do show ip prefix-list ");
else if (afi == AFI_IP6)
    snprintf(command_line, sizeof(command_line),
             "do show ipv6 prefix-list ");
else
    return; /* unsupported AFI */
Prompt To Fix With AI
This is a comment left during a code review.
Path: vtysh/vtysh.c
Line: 3483-3490

Comment:
**`command_line` is uninitialized when `afi` is neither `AFI_IP` nor `AFI_IP6`**

The `if/else if` block initializes `command_line` only for `AFI_IP` and `AFI_IP6`. If any other AFI value is passed, `command_line` remains uninitialized. The subsequent `strlcat` calls scan for a null terminator in an uninitialized buffer — undefined behavior that can produce a corrupted command string sent to every connected daemon.

An `else` fallback or an upfront initialization with `snprintf`/`memset` would prevent this. For example:

```c
char command_line[128] = {0};

if (afi == AFI_IP)
    snprintf(command_line, sizeof(command_line),
             "do show ip prefix-list ");
else if (afi == AFI_IP6)
    snprintf(command_line, sizeof(command_line),
             "do show ipv6 prefix-list ");
else
    return; /* unsupported AFI */
```

How can I resolve this? If you propose a fix, please make it concise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants