If you change the structure of the permalink, you have to provide the rewrite rules to understand the new structure. Also, you are not building the attachment permalink correctly. The next code is working:
add_filter( 'attachment_link', 'wpd_attachment_link', 20, 2 );
function wpd_attachment_link( $link, $attachment_id ){
$attachment = get_post( $attachment_id );
// Only for attachments actually attached to a parent post
if( ! empty( $attachment->post_parent ) ) {
$parent_link = get_permalink( $attachment->post_parent );
// make the link compatible with permalink settings with or without "/" at the end
$parent_link = rtrim( $parent_link, "/" );
$link = $parent_link . '/gallery/' . $attachment_id;
}
return $link;
}
add_action( 'init', function() {
// Tell WordPress how to handle the new structure
add_rewrite_rule( '(.+)/gallery/([0-9]{1,})/?$', 'index.php?attachment_id=$matches[2]', 'top' );
} );
Remember to flush the rewrite rules before trying this code (go to permalink settgins page in the backend and click on the sabe button).